Regex Demo This is a recap of WWDC’s Swift
@Metadata { @CallToAction( purpose: link, url: “https://developer.apple.com/wwdc22/110357 “) }
Overview
Topics Regex and Regex Builder Regex is Swift Standard Library
build in Type. @Row { @Column{
1 2 3 4 5 6 7 import RegexBuilderRegex { "Hi, WWDC" Repeat (.digit, count: 2 ) "!" }
}
@Column {
1 2 3 4 let regex = / user_id:\s* (\d+ )/ let regex2 = try Regex (#"user_id:\s*(\d+)"# )
}
}
@TabNavigator { @Tab(“Regex Builder”) {
1 2 3 4 5 6 7 import RegexBuilderRegex { "Hi, WWDC" Repeat (.digit, count: 2 ) "!" }
}
@Tab("Regex Literial") {
1 2 let regex = / user_id:\s* (\d+ )/
}
@Tab("From String") {
1 2 let regex2 = try Regex (#"user_id:\s*(\d+)"# )
}
}
Match
firstMatch
wholeMatch
prefixMatch
start(with: )
replacing(regex, with: )
trimmingPrefix()
split(separator: Regex)
switch case Regex:
Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let input = "name: John Appleseed, user_id: 100" let regex = Regex { "user_id:" ZeroOrMore (.whitespace) Capture { OneOrMore (.digit) } } if let match = input.firstMatch(of: regex) { print ("Matched: \(match.0 ) " ) print ("User ID: \(match.1 ) " ) } print ("wholeMatch:" , input.wholeMatch(of: regex))print ("prefixMatch" ,input.prefixMatch(of: regex))print ("starts:" , input.starts(with: regex))print ("replacing:" , input.replacing(regex, with: "456" ))print ("trimming:" , input.trimmingPrefix(regex))let regexSplitter = Regex { ZeroOrMore (.whitespace) "," ZeroOrMore (.whitespace) } print ("split" , input.split(separator:regexSplitter))
Result
1 2 3 4 5 6 7 8 Matched: user_id: 100 User ID: 100 wholeMatch: nil prefixMatch nil starts: false replacing: name: John Appleseed, 456 trimming: name: John Appleseed, user_id: 100 split ["name: John Appleseed", "user_id: 100"]
Regex support in Foundation Inorder to use Foundation’s default parser, you need to include Foundation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import Foundationimport RegexBuilderlet funcNameRegex = Regex { CharacterClass ("a" ... "z" , "A" ... "Z" ) ZeroOrMore { CharacterClass ("a" ... "z" , "A" ... "Z" , "0" ... "9" ) } } enum TestResult : String { case started case passed case failed case unknown } let testRegex = Regex { "Test Suite '" Capture (funcNameRegex) "' " TryCapture { ChoiceOf { "started" "passed" "failed" } } transform: { return TestResult (rawValue: String ($0 )) } " at " Capture ( .iso8601(timeZone: .current, includingFractionalSeconds: true , dateTimeSeparator: .space) ) Optionally ("." ) } let testSuiteTestInputs = [ "Test Suite 'RegexDSLTests' started at 2022-06-06 09:41:00.001" , "Test Suite 'RegexDSLTests' failed at 2022-06-06 09:41:00.001." , "Test Suite 'RegexDSLTests' passed at 2022-06-06 09:41:00.001." ] for line in testSuiteTestInputs { if let (_ , name, status, date) = line.wholeMatch(of: testRegex)? .output { print ("Matched: " , name, status, date, separator: ", " ) } }
Reuse an existing parser strtod
C function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import Darwinimport RegexBuilderstruct CDoubleParser : CustomConsumingRegexComponent { typealias RegexOutput = Double func consuming ( _ input : String , startingAt index : String .Index , in bounds : Range <String .Index >) throws -> (upperBound: String .Index , output: Double )? { input[index... ].withCString { startAddress in var endAddress: UnsafeMutablePointer <CChar >! let output = strtod(startAddress, & endAddress) guard endAddress > startAddress else {return nil } let parsedLength = startAddress.distance(to: endAddress) let upperBound = input.utf8.index(index, offsetBy: parsedLength ) return (upperBound, output) } } } let testCaseWithDurationInput = "Test Case '-[RegexDSLTests testCharacterClass]' passed (0.001 seconds)." let testCaseWithDurationRegex = Regex { "Test Case " OneOrMore (.any, .reluctant) "(" Capture { CDoubleParser () } " seconds)." } if let match = testCaseWithDurationInput.wholeMatch(of: testCaseWithDurationRegex) { print ("Time: \(match.1 ) " ) }