如何创建输出自定义类型的 Swift Regex?

2023-12-02

In the 全球开发者大会视频,结果表明你可以这样做Captures/TryCapture正则表达式生成器中的 s:

let regex = Regex {
  // ...

  TryCapture {
    OneOrMore(.digit)
  } transform: {
    Int($0)
  }

  // ...
}

和输出Regex将是类型安全的。这Regex将输出一个Int对于该组,而不是Substring像平常一样。

但是,我想做的是改变整个输出类型Regex,就像应用一个transform:在结束时Regex关闭。例如,要解析包含一个人的姓名、年龄和出生日期的行:

John (30) 1992-09-22

我想做这样的事情:

// this doesn't work and is just for illustration - there is no such Regex.init
let regex = Regex {
    Capture(/\w+/)
    " ("
    TryCapture(/\d+/) { Int($0) }
    ") "
    Capture(.iso8601Date(timeZone: .gmt))
} transform: { (_, name, age, dob) in
    Person(name: String(name), age: age, dob: dob)
}

我希望regex属于类型Regex<Person>, 并不是Regex<(Substring, Substring, Int, Date)>。那是,someString.wholeMatch(of: regex).output将是一个字符串,而不是一个元组。

我基本上只是想减少元组的出现,因为我发现使用它们非常不方便,尤其是未命名的元组。自从RegexComponent由无约束参数化RegexOutput类型,并且有内置类型,其中RegexOutput is Date and Decimal,使用正则表达式对任意类型执行此操作当然不是不可能的,对吗?

我的尝试是:

struct Person {
    let name: String
    let age: Int
    let dob: Date
}
let line = "John (30) 1992-09-22"
let regex = Regex {
    Capture {
        Capture(/\w+/)
        " ("
        TryCapture(/\d+/) { Int($0) }
        ") "
        Capture(.iso8601Date(timeZone: .gmt))
    } transform: { (_, name, age, dob) in
        Person(name: String(name), age: age, dob: dob)
    }
}
line.wholeMatch(of: regex)

但这在运行时崩溃了,给出了以下消息:

无法将类型“Swift.Substring”(0x7ff865e3ead8)的值转换为“(Swift.Substring,Swift.Substring,Swift.Int,Foundation.Date)”(0x7ff863f2e660)。

我的另一个尝试使用CustomConsumingRegexComponent显示在这个答案中,但这有一个很大的警告,即它不能正确回溯。

我怎样才能创建一个Regex输出我自己的类型?


根据我在样本中读到/看到的内容(例如快速正则表达式),创建一个类似于的正则表达式组件可能是个好主意.word, .digit,但是嵌套captures似乎工作并不容易。

这是在操场上运行的示例,用于创建Person struct实例:

public static func regexBuilderMatching(string: String = "John (30) 1992-09-22") {

    struct Person: CustomStringConvertible {
        let name: String
        let age: Int
        let dob: Date

        public func dobToFormatterString() -> String {
            let dateFormatter = DateFormatter()
            // 1992-09-22 04:00:00 +0000
            dateFormatter.dateFormat = "yyyy-MM-dd"
            return dateFormatter.string(from: self.dob)
        }
        
        var description: String {
            return "\(name), age: \(age), has dob: \(dobToFormatterString())"
        }
    }

    func dateFromString(dateString: String) -> Date? {
        let formatter = DateFormatter()
        formatter.timeStyle = .none // removes time from date
        formatter.dateStyle = .full
        formatter.dateFormat = "y-MM-d" // 1992-09-22
        return formatter.date(from: dateString)
    }

    let regexWithBasicCapture = Regex {
        /* 1. */ Capture { OneOrMore(.word) }
        /* 2. */ " ("
        /* 3. */ TryCapture { OneOrMore(.digit) }
                    transform: { match in
                        Int(match)
                    }
        /* 4. */ ") "
        /* 5. */ TryCapture { OneOrMore(.iso8601Date(timeZone: .gmt)) }
                    transform: { match in
                        dateFromString(dateString: String(match))
                    }
    }

    let matches = string.matches(of: regexWithBasicCapture)
    for match in matches {
        // shorthand syntax using match output
        // https://developer.apple.com/documentation/swift/regex/match
        let (_, name, age, date) = match.output
        let person = Person(name: String(name), age: age, dob: date)
        print(person)
    }
}

上面的代码将输出:

John, age: 30, has dob: 1992-09-22
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何创建输出自定义类型的 Swift Regex? 的相关文章

随机推荐