Swift 5.0+ Unit Tests codecov Swift Package Manager Compatible Linux Compatible

RegEx

RegExNSRegularExpression 的一个轻量级封装,用于在 Swift 中更轻松地进行正则表达式测试、数据提取和替换。

RegEx

特性

生成的 Match 结构包含 完整匹配、任何 捕获组 以及相应的 Swift 字符串范围

通过使用 Range<String.Index>SubstringRegEx.Match 能够返回所有这些信息,而无需复制输入字符串中的数据 👏

用法

给定一个字符串

let str = "16^32=2^128"

定义一个表达式,例如识别指数表示法 (^),同时捕获指数值

let expression = "\\d+\\^(\\d+)"

使用正则表达式

let regex = try RegEx(pattern: expression)

regex.test(str) // true

regex.numberOfMatches(in: str) // 2

let first = regex.firstMatch(in: str) // 1 match with 1 captured group
first?.values // ["16^32", "32"] 

let matches = regex.matches(in: str) // 2 matches with 1 captured group each
matches[0].values // ["16^32", "32"] 
matches[1].values // ["2^128", "128"]

迭代匹配项

let iterator = regex.iterator(for: str) // Iterate over matches one by one
iterator.next()?.values // ["16^32", "32"] 
iterator.next()?.values // ["2^128", "128"]
iterator.next()         // nil

使用模板替换

let regex = try RegEx(pattern: #"(\d)(\d)"#)
let result = regex.replaceMatches(in: "1234", withTemplate: "$2$1")
// result: 2143

使用自定义逻辑替换

let regex = try RegEx(pattern: #"(\w+)\b"#)
let result = regex.replaceMatches(in: "Hello world!")  { match in 
    let value = String(match.values[0] ?? "")
    return String(value.reversed())
}
// result: olleH dlrow!

安装

没有框架,只需复制粘贴!

public class RegEx {
    private let regex: NSRegularExpression

    public init(pattern: String, options: NSRegularExpression.Options = []) throws {
        regex = try NSRegularExpression(pattern: pattern, options: options)
    }

    public struct Match {
        public let values: [Substring?]
        public let ranges: [Range<String.Index>?]
    }

    public func numberOfMatches(in string: String, from index: String.Index? = nil) -> Int {
        let startIndex = index ?? string.startIndex
        let range = NSRange(startIndex..., in: string)
        return regex.numberOfMatches(in: string, range: range)
    }

    public func firstMatch(in string: String, from index: String.Index? = nil) -> Match? {
        let startIndex = index ?? string.startIndex
        let range = NSRange(startIndex..., in: string)
        let result = regex.firstMatch(in: string, range: range)
        return result.flatMap { map(result: $0, in: string) }
    }

    public func matches(in string: String, from index: String.Index? = nil) -> [Match] {
        let startIndex = index ?? string.startIndex
        let range = NSRange(startIndex..., in: string)
        let results = regex.matches(in: string, range: range)
        return results.map { map(result: $0, in: string) }
    }

    public func test(_ string: String) -> Bool {
        return firstMatch(in: string) != nil
    }

    func map(result: NSTextCheckingResult, in string: String) -> Match {
        let ranges = (0..<result.numberOfRanges).map { index in
            Range(result.range(at: index), in: string)
        }
        let substrings = ranges.map { $0.flatMap { string[$0] }}
        return Match(values: substrings, ranges: ranges)
    }

}


extension RegEx {
    public class Iterator: IteratorProtocol {
        let regex: RegEx
        let string: String
        var current: RegEx.Match?

        init(regex: RegEx, string: String) {
            self.regex = regex
            self.string = string
            current = regex.firstMatch(in: string)
        }

        public func next() -> RegEx.Match? {
            defer {
                current = current.flatMap {
                    let index = $0.ranges[0]?.upperBound
                    return self.regex.firstMatch(in: self.string, from: index)
                }
            }
            return current
        }
    }

    public func iterator(for string: String) -> Iterator {
        return Iterator(regex: self, string: string)
    }
}

Swift 包管理器

实际上,我喜欢单元测试,所以我把这个仓库做成了一个 Swift 包,可以使用 Swift 包管理器导入和使用。

将以下代码添加到您的 Package.swift

dependencies: [
    .package(url: "https://github.com/eneko/RegEx.git", from: "0.1.0")
],
targets: {
    .target(name: "YourTarget", dependencies: ["RegEx"])
}

单元测试

如果您好奇,可以使用 $ swift test$ swift test --parallel 运行测试。