SwiftRegexDSL

License Language

我们大多数 Swift 开发者并非每天都在使用和创建正则表达式。但每次我们需要用到时,都会依赖于网络搜索和旧文档。然后,我们不得不处理不安全的代码,并在达到预期结果之前进行多次尝试。这感觉就像在使用 Swift 这样的现代语言进行编码时,一次沉重的回滚。

这里我们可以强调两个问题

SwiftRegexDSL 由此诞生,它是一种用于 Swift 中正则表达式的声明式结构化语言。 它的想法是利用与 SwiftUI 相同的 "魔法"、Result Builder(https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)来实现 regex。 除了带来编译时检查之外,该 DSL 还提供可读的表达式,更适合于组合和控制流。 总而言之,可以减少正则表达式带来的麻烦!

struct ThisIsARegex: Regex {
  let shouldMatchLine: Bool

  var body: Regex {
    "Hello"
    WhiteSpace()
    "World,"
    if shouldMatchLine {
      Line()
    }
    AnyCharacter()
      .oneOrMore()
  }
}
...
let regex = ThisIsARegex(shouldMatchLine: false)
"Hello World, how...".match(regex) // true

安装

SwiftRegex 打包为 Swift 包,您可以简单地从 Xcode 中的 File > Swift Packages > Add Package Dependency 添加到您的 iOS 或 macOS 项目中,然后查找 https://github.com/kodlian/SwiftRegexDSL.git

如果您在 Xcode 之外进行操作,请将其添加到 Package.swift 中的 dependencies 部分

dependencies: [

dependencies: [
    .package(url: "https://github.com/kodlian/SwiftRegexDSL.git", .upToNextMajor(from: "1.0.0"))
]

如何使用?

就像 SwiftUI 视图一样,您可以将您的 regex 声明为一个结构体,最好放在一个单独的文件中,并使用 body 来构建 regex。

import SwiftRegexDSL

struct MyRegex: Regex {
  var body: Regex {
    Digit()
     .oneOrMore()  
    Word()
  }
}

您还可以使用 @RegexBuilder 注解任何变量或函数。

@RegexBuilder
var digits: Regex {
  Digit()
   ..exactly(10) 
}

模式 (Pattern)

SwiftRegexDSL 实现了在 ICU API 中最常见的模式,该 API 受 Swift Foundation 支持。

基础

支持最常见的字符类和特殊字符,例如 AnyCharacterDigitNotDigitWordWhitespace。 请参阅 CharacterClass.swiftSpecialCharacters.swift 以查看完整的覆盖范围。

当然,您可以在您的 regex body 中添加任何字符串,可以直接添加或使用 Text regex。

var body: Regex {
 "Title"
 Text("-") 
  .quantified(0..<2)
 Digit()
}

请注意,String 本身不是 Regex 组件,而是一个可转换为 Regex 的表达式。 这意味着如果您需要应用修饰符,请将其包装在 Text 中。

量词 (Quantifier)

您可以使用 quantified(...) 修饰符或任何快捷方式 zeroOrMoreoneOrMorezeroOrOneexactly 来指定模式应匹配的次数。

var body: Regex {
 Text("-")
   .zeroOrOne()  
 Digit()
   .quantified(1..<4)
}

分组和断言 (Group and Assertion)

可以使用 Group 将用于提高可读性和应用修饰符的分组添加到 regex 的 body 中。

Digit()
Group {
   Word()
   Digit()
}.zeroOrMore()
...

此外,DSL 还支持

Regex 集合 (Set)

可以使用 Character 数组、Swift Set 或者字符范围定义集合。

Digit()
[`a`,`c`...`z`]
...

可以使用 ExclusionSet 结构创建排除集合。

锚点 (Anchor)

可以使用 StartAnchorEndAnchor 将用于匹配输入字符串特定区域的锚点添加到 body 中

StartAnchor.line
Digit()
...

Unicode

DSL 支持按 Unicode 名称、十六进制或属性使用 UnsafeUnicode 定义的模式。 虽然它被认为是不安全的,因为参数是字符串,而不是十六进制绑定。

组合、参数化和自定义 (Composition, Parametrisation and Custom)

SwiftRegexDSl 设计为可扩展的,您可以使用其他 regexes 组合您的 regex

import SwiftRegexDSL

struct DomainRegex: Regex { ... }
struct ExtensionRegex: Regex { ... }

struct HostRegex: Regex {
  var body: Regex {
     DomainRegex()
     "."
     ExtensionRegex()
  }
}

regex 被定义为 Struct,并且 DSL 支持控制流,因此很容易将参数定义为类型属性

import SwiftRegexDSL

struct TitleRegex: Regex {

  let shouldStartWithDigit: Bool
  
  var body: Regex {
    if shouldStartWithDigit {
      Digit()
        .oneOrMore()  
    }
    AnyCharacter()
      .oneOrMore()
  }
}

如果框架缺少某些内容(例如 regex 元字符),您可以在您的 regex body 中使用 UsafeRawText,因为 DSL 中的 Texts 会自动进行安全转义。 如果出现这种情况,请不要犹豫为框架做出贡献,以提高 regex 标准的覆盖范围。

Regex 用法

当您的 regex 准备就绪时,该框架在 String 上提供了各种扩展

也可以从 Regex 创建 NSRegularExpression

未来方向和结束语

这是一个年轻的项目,可以进行许多改进

最后,我不是 Regex 专家,因此创建了这个框架来减轻我使用它们的痛苦:所以我可能错过并犯了一些错误。 通过将其开源,我希望 swift 社区的贡献会将其提升到一个新的水平。