我们大多数 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)
}
SwiftRegexDSL 实现了在 ICU
API 中最常见的模式,该 API 受 Swift Foundation 支持。
支持最常见的字符类和特殊字符,例如 AnyCharacter
、Digit
、NotDigit
、Word
、Whitespace
。 请参阅 CharacterClass.swift
和 SpecialCharacters.swift
以查看完整的覆盖范围。
当然,您可以在您的 regex body 中添加任何字符串,可以直接添加或使用 Text
regex。
var body: Regex {
"Title"
Text("-")
.quantified(0..<2)
Digit()
}
请注意,String
本身不是 Regex
组件,而是一个可转换为 Regex 的表达式。 这意味着如果您需要应用修饰符,请将其包装在 Text
中。
您可以使用 quantified(...)
修饰符或任何快捷方式 zeroOrMore
、oneOrMore
、zeroOrOne
、exactly
来指定模式应匹配的次数。
var body: Regex {
Text("-")
.zeroOrOne()
Digit()
.quantified(1..<4)
}
可以使用 Group
将用于提高可读性和应用修饰符的分组添加到 regex 的 body 中。
Digit()
Group {
Word()
Digit()
}.zeroOrMore()
...
此外,DSL 还支持
Alternative
regex 的 A 或 B 模式LookAheadAssertion
、NegativeLookAheadAssertion
等的断言,用于匹配但不推进输入位置CaptureGroup
.options(...)
修饰符在任何模式上应用模式选项,例如 caseInsensitive
可以使用 Character
数组、Swift Set 或者字符范围定义集合。
Digit()
[`a`,`c`...`z`]
...
可以使用 ExclusionSet
结构创建排除集合。
可以使用 StartAnchor
或 EndAnchor
将用于匹配输入字符串特定区域的锚点添加到 body 中
StartAnchor.line
Digit()
...
DSL 支持按 Unicode 名称、十六进制或属性使用 UnsafeUnicode
定义的模式。 虽然它被认为是不安全的,因为参数是字符串,而不是十六进制绑定。
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 准备就绪时,该框架在 String
上提供了各种扩展
.match(regex)
检查字符串是否与 regex 匹配.range(of: regex)
查找字符串中匹配 regex 的范围.capturedGroupsRanges(of: regex, with: "foo")
.replacingOccurrences(of: regex, with: "foo")
替换字符串的一部分也可以从 Regex
创建 NSRegularExpression
。
这是一个年轻的项目,可以进行许多改进
25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?
创建一个匹配数字的闭合范围的 regex 将节省时间。Character
,对于数字,我们现在需要使用此数字的 Character
表示形式。最后,我不是 Regex 专家,因此创建了这个框架来减轻我使用它们的痛苦:所以我可能错过并犯了一些错误。 通过将其开源,我希望 swift 社区的贡献会将其提升到一个新的水平。