一个 Swift 的正则表达式库
此库使用 NSRegularExpression 来执行正则表达式模式匹配的实际逻辑。 但是,它提供了一个更简洁的接口,并且专门设计为充分利用 Swift。 正则表达式支持的语法可以在这里找到。您还应该查看 NSRegularExpression.Options 和 NSRegularExpression.MatchingOptions。 我建议使用 https://regex101.com/ 来测试您的正则表达式模式。
将其添加到您的 Podfile
pod 'RegularExpressions', :git => 'https://github.com/Peter-Schorn/RegularExpressions.git'
String.regexMatch
、String.regexFindAll
、String.regexSub
和 String.regexSplit
都接受符合 RegexProtocol
的对象。 此对象包含有关正则表达式的信息,包括
var pattern: String { get }
- 正则表达式模式。var regexOptions: NSRegularExpression.Options { get }
- 正则表达式选项 (参见 NSRegularExpression.Options)。var matchingOptions: NSRegularExpression.MatchingOptions { get }
- 参见 NSRegularExpression.MatchingOptions。var groupNames: [String]? { get }
- 捕获组的名称。RegexProtocol
还定义了许多便利方法
func asNSRegex() throws -> NSRegularExpression
将自身转换为 NSRegularExpression。
func numberOfCaptureGroups() throws -> Int
返回正则表达式中捕获组的数量。
func patternIsValid() -> Bool
如果正则表达式模式有效,则返回 true。 否则返回 false。
NSRegularExpression
已被扩展为符合 RegexProtocol
,但它对 matchingOptions
和 groupNames
属性**总是**返回 []
和 nil
。 使用 Regex
或符合 RegexProtocol
的其他类型来自定义这些选项。
此库提供的 Regex
结构体符合 RegexProtocol
,并且是创建可与此库中的方法一起使用的正则表达式对象的最简单方法。
init(
pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = [],
groupNames: [String]? = nil
) throws
如果模式无效则抛出。
init(
_ pattern: String,
_ regexOptions: NSRegularExpression.Options = []
) throws
如果模式无效则抛出。
init(
nsRegularExpression: NSRegularExpression,
matchingOptions: NSRegularExpression.MatchingOptions = [],
groupNames: [String]? = nil
)
从 NSRegularExpression
创建 Regex
对象。
String.regexMatch
和 String.regexFindAll
都使用 RegexMatch
结构体来保存有关正则表达式匹配的信息。 它包含以下属性
let sourceString: Substring
- 被匹配的字符串。 使用子字符串是为了减少内存使用。 请注意,SubString
呈现与 String
相同的界面。let fullMatch: String
- 模式在源字符串中的完整匹配项。let range: Range<String.Index>
- 完整匹配项在源字符串中的范围。let groups: [RegexGroup?]
- 捕获组。RegexMatch
还有一个按名称检索组的方法
func group(named name: String) -> RegexGroup?
如果未找到名称,**或者**由于在正则表达式模式中指定为可选,因此该函数将返回 nil。
RegexGroup
结构体保存有关捕获组的信息,它具有以下属性
let name: String?
- 捕获组的名称。let match: String
- 匹配的捕获组。let range: Range<String.Index>
- 捕获组在源字符串中的范围。String.regexMatch
将返回字符串中正则表达式的第一个匹配项,如果未找到匹配项,则返回 nil。 它有两个重载
func regexMatch<RegularExpression: RegexProtocol>(
_ regex: RegularExpression,
range: Range<String.Index>? = nil
) throws -> RegexMatch?
func regexMatch(
_ pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = [],
groupNames: [String]? = nil,
range: Range<String.Index>? = nil
) throws -> RegexMatch?
pattern
、regexOptions
、matchingOptions
和 groupNames
参数对应于 RegexProtocol 的实例属性。
range
表示在其中搜索模式的字符串的范围。
如果模式无效,或者组名称的数量与捕获组的数量不匹配,这些方法将抛出 (参见 RegexError)。 如果未找到匹配项,它们将**永远不会**抛出错误。
有关这些函数返回的 RegexMatch
的信息,请参阅 提取匹配项和捕获组。
警告:如果您更改源字符串,则匹配项和捕获组的范围可能会失效。 使用 String.regexsub 执行多个替换。
示例
var inputText = "name: Chris Lattner"
// If you use comments in the pattern,
// you MUST use `.allowCommentsAndWhitespace` for the `regexOptions`
let pattern = #"""
name: # the literal string 'name'
\s+ # one more more whitespace characters
([a-z]+) # one or more lowercase letters
\s+ # one more more whitespace characters
([a-z]+) # one or more lowercase letters
"""#
// create the regular expression object
let regex = try! Regex(
pattern: pattern,
regexOptions: [.caseInsensitive, .allowCommentsAndWhitespace],
groupNames: ["first name", "last name"]
// the names of the capture groups
)
if let match = try inputText.regexMatch(regex) {
print("full match: '\(match.fullMatch)'")
print("first capture group: '\(match.groups[0]!.match)'")
print("second capture group: '\(match.groups[1]!.match)'")
// perform a replacement on the first capture group
inputText.replaceSubrange(
match.groups[0]!.range, with: "Steven"
)
print("after replacing text: '\(inputText)'")
}
// full match: 'name: Chris Lattner'
// first capture group: 'Chris'
// second capture group: 'Lattner'
// after replacing text: 'name: Steven Lattner'
let inputText = """
Man selects only for his own good: \
Nature only for that of the being which she tends.
"""
let pattern = #"Man selects ONLY FOR HIS OWN (\w+)"#
let searchRange =
(inputText.startIndex)
..<
(inputText.index(inputText.startIndex, offsetBy: 40))
let match = try inputText.regexMatch(
pattern,
regexOptions: [.caseInsensitive],
matchingOptions: [.anchored], // anchor matches to the beginning of the string
groupNames: ["word"], // the names of the capture groups
range: searchRange // the range of the string in which to search for the pattern
)
if let match = match {
print("full match:", match.fullMatch)
print("capture group:", match.group(named: "word")!.match)
}
// full match: Man selects only for his own good
// capture group: good
String.regexFindAll
将返回字符串中正则表达式的所有匹配项,如果未找到匹配项,则返回一个空数组。 它与 String.regexMatch
具有完全相同的重载
func regexFindAll<RegularExpression: RegexProtocol>(
_ regex: RegularExpression,
range: Range<String.Index>? = nil
) throws -> [RegexMatch]
func regexFindAll(
_ pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = [],
groupNames: [String]? = nil,
range: Range<String.Index>? = nil
) throws -> [RegexMatch]
警告:如果您更改源字符串,则匹配项和捕获组的范围可能会失效。 使用 String.regexsub 执行多个替换。
pattern
、regexOptions
、matchingOptions
和 groupNames
参数对应于 RegexProtocol 的实例属性。
与 String.regexMatch
一样,range
表示在其中搜索模式的字符串的范围。
如果模式无效,或者组名称的数量与捕获组的数量不匹配,这些方法将抛出 (参见 RegexError)。 如果未找到匹配项,它们将**永远不会**抛出错误。
有关这些函数返回的 RegexMatch
的信息,请参阅 提取匹配项和捕获组。
示例
var inputText = "season 8, EPISODE 5; season 5, episode 20"
// create the regular expression object
let regex = try Regex(
pattern: #"season (\d+), Episode (\d+)"#,
regexOptions: [.caseInsensitive],
groupNames: ["season number", "episode number"]
// the names of the capture groups
)
let results = try inputText.regexFindAll(regex)
for result in results {
print("fullMatch: '\(result.fullMatch)'")
print("capture groups:")
for captureGroup in result.groups {
print(" \(captureGroup!.name!): '\(captureGroup!.match)'")
}
print()
}
let firstResult = results[0]
// perform a replacement on the first full match
inputText.replaceSubrange(
firstResult.range, with: "new value"
)
print("after replacing text: '\(inputText)'")
// fullMatch: 'season 8, EPISODE 5'
// capture groups:
// 'season number': '8'
// 'episode number': '5'
//
// fullMatch: 'season 5, episode 20'
// capture groups:
// 'season number': '5'
// 'episode number': '20'
//
// after replacing text: 'new value; season 5, episode 20'
String.regexSplit
将通过模式出现的位置分割字符串。
func regexSplit(
_ pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = [],
ignoreIfEmpty: Bool = false,
maxLength: Int? = nil,
range: Range<String.Index>? = nil
) throws -> [String]
func regexSplit<RegularExpression: RegexProtocol>(
_ regex: RegularExpression,
ignoreIfEmpty: Bool = false,
maxLength: Int? = nil,
range: Range<String.Index>? = nil
) throws -> [String]
pattern
、regexOptions
、matchingOptions
和 groupNames
参数对应于 RegexProtocol 的实例属性。
ignoreIfEmpty
- 如果为 true,则所有空字符串将从数组中删除。 如果为 false(默认),则将包括它们。maxLength
- 返回数组的最大长度。 如果为 nil(默认),则字符串将按模式的每个出现位置进行分割。示例
let colors = "red,orange,yellow,blue"
let array = try colors.regexSplit(",")
print(array)
// array = ["red", "orange", "yellow", "blue"]
let colors = "red and orange ANDyellow and blue"
// create the regular expression object
let regex = try Regex(#"\s*and\s*"#, [.caseInsensitive])
let array = try colors.regexSplit(regex, maxLength: 3)
print(array)
// array = ["red", "orange", "yellow"]
// note that "blue" is not returned because the length of the
// array was limited to 3 items.
String.regexSub
和 String.regexSubInPlace
将执行正则表达式替换。 它们具有完全相同的参数和重载。
func regexSub(
_ pattern: String,
with template: String = "",
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = [],
range: Range<String.Index>? = nil
) throws -> String
func regexSub<RegularExpression: RegexProtocol>(
_ regex: RegularExpression,
with template: String = "",
range: Range<String.Index>? = nil
) throws -> String
pattern
、regexOptions
、matchingOptions
和 groupNames
参数对应于 RegexProtocol 的实例属性。
with
- 用于替换匹配模式的模板字符串。 有关如何格式化模板,请参见 模板匹配格式。 默认为空字符串。示例
let name = "Peter Schorn"
// The .anchored matching option only looks for matches
// at the beginning of the string.
// Consequently, only the first word will be matched.
let regexObject = try Regex(
pattern: #"\w+"#,
regexOptions: [.caseInsensitive],
matchingOptions: [.anchored]
)
let replacedText = try name.regexSub(regexObject, with: "word")
print(replacedText)
// replacedText = "word Schorn"
let name = "Charles Darwin"
let reversedName = try name.regexSub(
#"(\w+) (\w+)"#,
with: "$2 $1"
// $1 and $2 represent the
// first and second capture group, respectively.
// $0 represents the entire match.
)
print(reversedName)
// reversedName = "Darwin Charles"
如果您需要进一步自定义正则表达式替换,可以使用以下方法
func regexSub<RegularExpression: RegexProtocol>(
_ regex: RegularExpression,
range: Range<String.Index>? = nil,
replacer: (_ matchIndex: Int, _ match: RegexMatch) -> String?
) throws -> String
func regexSub(
_ pattern: String,
regexOptions: NSRegularExpression.Options = [],
matchingOptions: NSRegularExpression.MatchingOptions = [],
groupNames: [String]? = nil,
range: Range<String.Index>? = nil,
replacer: (_ matchIndex: Int, _ match: RegexMatch) -> String?
) throws -> String
pattern
、regexOptions
、matchingOptions
和 groupNames
参数对应于 RegexProtocol 的实例属性。
replacer
- 一个闭包,它接受正则表达式匹配项的索引和正则表达式匹配项,并返回一个用于替换它的新字符串。 从闭包中返回 nil 以指示不应更改匹配项。示例
let inputString = """
Darwin's theory of evolution is the \
unifying theory of the life sciences.
"""
let pattern = #"\w+"# // match each word in the input string
let replacedString = try inputString.regexSub(pattern) { indx, match in
if indx > 5 { return nil } // only replace the first 5 matches
return match.fullMatch.uppercased() // uppercase the full match
}
print(replacedString)
// replacedString = """
// DARWIN'S THEORY OF EVOLUTION IS the \
// unifying theory of the life sciences.
// """
如果您需要为每个单独的捕获组执行替换,可以使用 RegexMatch
结构体的 replaceGroups
方法
func replaceGroups(
_ replacer: (
_ groupIndex: Int, _ group: RegexGroup
) -> String?
) -> String
replacer
- 一个闭包,它接受捕获组的索引和捕获组,并返回一个用于替换它的新字符串。 从闭包中返回 nil 以指示不应更改捕获组。示例
let inputText = "name: Peter, id: 35, job: programmer"
let pattern = #"name: (\w+), id: (\d+)"#
let groupNames = ["name", "id"]
let match = try inputText.regexMatch(
pattern, groupNames: groupNames
)!
let replacedMatch = match.replaceGroups { indx, group in
if group.name == "name" { return "Steven" }
if group.name == "id" { return "55" }
return nil
}
print(replacedMatch)
// match.fullMatch = "name: Peter, id: 35"
// replacedMatch = "name: Steven, id: 55"
您可以按以下方式将上述方法组合在一起
let inputString = """
name: sally, id: 26
name: alexander, id: 54
"""
let regexObject = try Regex(
pattern: #"name: (\w+), id: (\d+)"#,
groupNames: ["name", "id"]
)
let replacedText = try inputString.regexSub(regexObject) { indx, match in
if indx == 0 { return nil }
return match.replaceGroups { indx, group in
if group.name == "name" {
return group.match.uppercased()
}
if group.name == "id" {
return "redacted"
}
return nil
}
}
print(replacedText)
// replacedText = """
// name: sally, id: 26
// name: ALEXANDER, id: redacted
// """
模式匹配运算符 ~=
已被重载,以支持在 switch 语句中检查正则表达式的匹配项。 例如
let inputStrig = #"user_id: "asjhjcb""#
switch inputStrig {
case try Regex(#"USER_ID: "[a-z]+""#, [.caseInsensitive]):
print("valid user id")
case try? Regex(#"[!@#$%^&]+"#):
print("invalid character in user id")
case try! Regex(#"\d+"#):
print("user id cannot contain numbers")
default:
print("no match")
}
// prints "valid user id"
可以根据您希望如何处理由无效正则表达式模式引起的错误,使用 try
、try?
和 try!
。 遗憾的是,无法将正则表达式模式的匹配项绑定到变量。