使用Swift Regex Builder按名称访问可选捕获组的方法
Swift Regex Builder:安全访问可选捕获组的匹配结果
问题场景
输入行仅两种格式:要么是单个姓名单词,要么是以Test 开头后接姓名。需要提取姓名,同时无需通过匹配索引,安全访问Test 的匹配结果(不存在时为nil)。当前代码在匹配无Test 的行时会崩溃,原因是未匹配到的可选捕获组通过引用访问时发生类型转换错误。
原代码示例
import RegexBuilder let line1 = "Test John" let line2 = "Robert" let nameReference = Reference(String.self) let testReference = Reference(String.self) let regex = Regex { Optionally { Capture(as:testReference) { "Test " } transform : { text in String(text) } } Capture(as:nameReference) { OneOrMore(.any) } transform : { text in String(text) } } if let matches = try? regex.wholeMatch(in: line1) { // 可切换为line2测试 let theName = matches[nameReference] print("Name is \(theName)") // 通过索引访问正常 if let flag = matches.1, flag == "Test " { print("Using index: This is a test line") } else { print("Using index: Not a test line") } // 通过引用访问在line2时崩溃 if matches[testReference] == "Test " { print("Using reference: This is a test line") } else { print("Using reference: Not a test line") } }
运行结果
- 匹配
line1时输出正常:
Name is John Using index: This is a test line Using reference: This is a test line
- 匹配
line2时崩溃,输出:
Name is Robert Using index: Not a test line Could not cast value of type 'Swift.Optional<Swift.Substring>' (0x7ff84bf06f20) to 'Swift.String' (0x7ff84ba6e918).
解决方案
问题核心:可选捕获组的匹配结果本质是可选类型,但原代码声明Reference(String.self)未体现可选性,导致未匹配时强制转换失败。以下两种方式可解决:
方式一:声明可选类型的Reference
将testReference声明为Reference(String?.self),让未匹配时返回nil而非崩溃:
import RegexBuilder let line1 = "Test John" let line2 = "Robert" let nameReference = Reference(String.self) // 声明为可选String类型的引用 let testReference = Reference(String?.self) let regex = Regex { Optionally { Capture(as: testReference) { "Test " } transform: { text in String(text) } } Capture(as: nameReference) { OneOrMore(.any) } transform: { text in String(text) } } if let matches = try? regex.wholeMatch(in: line2) { let theName = matches[nameReference] print("Name is \(theName)") // 安全访问testReference,无崩溃 if matches[testReference] == "Test " { print("Using reference: This is a test line") } else { print("Using reference: Not a test line") } }
方式二:使用命名捕获组结合AnyRegexOutput
利用AnyRegexOutput的下标特性,通过捕获组名称访问,未匹配时返回nil:
import RegexBuilder let line1 = "Test John" let line2 = "Robert" let regex = Regex { Optionally { Capture(named: "testFlag") { "Test " } transform: { text in String(text) } } Capture(named: "name") { OneOrMore(.any) } transform: { text in String(text) } } if let matches = try? regex.wholeMatch(in: line2) as AnyRegexOutput { let theName = matches["name"] as! String print("Name is \(theName)") // 通过名称安全访问,未匹配时返回nil if matches["testFlag"] as? String == "Test " { print("Using named capture: This is a test line") } else { print("Using named capture: Not a test line") } }
关键说明
- 当捕获组被包裹在
Optionally内部时,匹配结果为可选值,Reference类型需对应Optional<T>才能安全访问。 AnyRegexOutput的下标支持通过捕获组名称访问,未匹配时返回nil,适合不想使用Reference的场景。
内容的提问来源于stack exchange,提问作者rene
相关产品推荐
相关产品推荐

