You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:30:41