Swift 提取正则表达式匹配项
Swift extract regex matches
我想从匹配正则表达式模式的字符串中提取子字符串。
所以我正在寻找这样的东西:
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
???
}
这就是我所拥有的:
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
var regex = NSRegularExpression(pattern: regex,
options: nil, error: nil)
var results = regex.matchesInString(text,
options: nil, range: NSMakeRange(0, countElements(text)))
as Array<NSTextCheckingResult>
/// ???
return ...
}
问题是,matchesInString
为我提供了一个 NSTextCheckingResult
的数组,其中 NSTextCheckingResult.range
的类型为 NSRange
。
NSRange
与 Range<String.Index>
不兼容,所以它阻止我使用 text.substringWithRange(...)
知道如何在 swift 中实现这个简单的事情而不需要太多代码行吗?
即使 matchesInString()
方法将 String
作为第一个参数,
它在内部与 NSString
一起工作,并且必须给出范围参数
使用 NSString
长度而不是 Swift 字符串长度。否则会
"extended grapheme clusters" 失败,例如 "flags".
自 Swift 4 (Xcode 9) 起,Swift 标准
库提供了在 Range<String.Index>
之间进行转换的函数
和 NSRange
.
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
String(text[Range([=10=].range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
示例:
let string = "€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
注意:强制展开Range([=23=].range, in: text)!
是安全的,因为
NSRange
指的是给定字符串 text
的子字符串。
但是,如果您想避免它,请使用
return results.flatMap {
Range([=12=].range, in: text).map { String(text[[=12=]]) }
}
相反。
(Swift 3 及更早版本的旧答案:)
所以你应该将给定的 Swift 字符串转换为 NSString
然后提取
范围。结果将自动转换为 Swift 字符串数组。
(Swift 1.2 的代码可以在编辑历史中找到。)
Swift 2 (Xcode 7.3.1) :
func matchesForRegexInText(regex: String, text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text,
options: [], range: NSMakeRange(0, nsString.length))
return results.map { nsString.substringWithRange([=13=].range)}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
示例:
let string = "€4€9"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]
Swift 3 (Xcode 8)
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: [=15=].range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
示例:
let string = "€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
我就是这样做的,我希望它能为 Swift.
带来新的视角
在下面的这个例子中,我将得到 []
之间的任何字符串
var sample = "this is an [hello] amazing [world]"
var regex = NSRegularExpression(pattern: "\[.+?\]"
, options: NSRegularExpressionOptions.CaseInsensitive
, error: nil)
var matches = regex?.matchesInString(sample, options: nil
, range: NSMakeRange(0, countElements(sample))) as Array<NSTextCheckingResult>
for match in matches {
let r = (sample as NSString).substringWithRange(match.range)//cast to NSString is required to match range format.
println("found= \(r)")
}
如果您想从字符串中提取子字符串,而不仅仅是位置(而是包含表情符号的实际字符串)。那么,以下可能是一个更简单的解决方案。
extension String {
func regex (pattern: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))
let nsstr = self as NSString
let all = NSRange(location: 0, length: nsstr.length)
var matches : [String] = [String]()
regex.enumerateMatchesInString(self, options: NSMatchingOptions(rawValue: 0), range: all) {
(result : NSTextCheckingResult?, _, _) in
if let r = result {
let result = nsstr.substringWithRange(r.range) as String
matches.append(result)
}
}
return matches
} catch {
return [String]()
}
}
}
用法示例:
"someText ⚽️ pig".regex("⚽️")
将return以下内容:
["⚽️"]
注意使用“\w+”可能会产生意外的“”
"someText ⚽️ pig".regex("\w+")
请问return这个String数组
["someText", "️", "pig"]
@p4bloch 如果你想从一系列捕获括号中捕获结果,那么你需要使用NSTextCheckingResult
的rangeAtIndex(index)
方法,而不是range
。这是来自上面的 Swift2 @MartinR 的方法,适用于捕获括号。在返回的数组中,第一个结果 [0]
是整个捕获,然后各个捕获组从 [1]
开始。我注释掉了 map
操作(这样更容易看到我更改了什么)并将其替换为嵌套循环。
func matches(for regex: String!, in text: String!) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text, options: [], range: NSMakeRange(0, nsString.length))
var match = [String]()
for result in results {
for i in 0..<result.numberOfRanges {
match.append(nsString.substringWithRange( result.rangeAtIndex(i) ))
}
}
return match
//return results.map { nsString.substringWithRange( [=10=].range )} //rangeAtIndex(0)
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
一个示例用例可能是,假设您想要拆分 title year
的字符串,例如 "Finding Dory 2016" 您可以这样做:
print ( matches(for: "^(.+)\s(\d{4})" , in: "Finding Dory 2016"))
// ["Finding Dory 2016", "Finding Dory", "2016"]
我的答案建立在给定答案的基础上,但通过添加额外的支持使正则表达式匹配更加稳健:
- Returns 不仅匹配,而且 returns 还匹配所有捕获组 (见下面的示例)
- 此解决方案支持可选匹配
,而不是返回空数组
- 通过不打印到控制台来避免
do/catch
并且 使用 guard
构造
- 将
matchingStrings
作为 扩展添加到 String
Swift 4.2
//: Playground - noun: a place where people can play
import Foundation
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.range(at: [=10=]).location != NSNotFound
? nsString.substring(with: result.range(at: [=10=]))
: ""
}
}
}
}
"prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])")
// Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]]
"prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["prefix12", "12"]]
"12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["12", "12"]], other answers return an empty array here
// Safely accessing the capture of the first match (if any):
let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1]
// Prints: Optional("12")
Swift 3
//: Playground - noun: a place where people can play
import Foundation
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.rangeAt([=11=]).location != NSNotFound
? nsString.substring(with: result.rangeAt([=11=]))
: ""
}
}
}
}
"prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])")
// Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]]
"prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["prefix12", "12"]]
"12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["12", "12"]], other answers return an empty array here
// Safely accessing the capture of the first match (if any):
let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1]
// Prints: Optional("12")
Swift 2
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matchesInString(self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.rangeAtIndex([=12=]).location != NSNotFound
? nsString.substringWithRange(result.rangeAtIndex([=12=]))
: ""
}
}
}
}
不幸的是,我发现已接受的答案的解决方案无法在 Swift 3 for Linux 上编译。这是修改后的版本:
import Foundation
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try RegularExpression(pattern: regex, options: [])
let nsString = NSString(string: text)
let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: [=10=].range) }
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
主要区别是:
Swift on Linux 似乎需要在没有 Swift-native 等价物的 Foundation 对象上删除 NS
前缀。 (参见 Swift evolution proposal #86。)
Swift on Linux 还需要为 RegularExpression
初始化和 matches
方法指定 options
参数。
出于某种原因,将 String
强制转换为 NSString
在 Swift 和 Linux 中不起作用,但初始化一个新的 NSString
与 String
因为来源确实有效。
此版本也适用于 macOS 上的 Swift 3 / Xcode,唯一的例外是您必须使用名称 NSRegularExpression
而不是 RegularExpression
.
这是一个非常简单的解决方案,returns 一个包含匹配项的字符串数组
Swift 3.
internal func stringsMatching(regularExpressionPattern: String, options: NSRegularExpression.Options = []) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regularExpressionPattern, options: options) else {
return []
}
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map {
nsString.substring(with: [=10=].range)
}
}
上面的大多数解决方案只给出完全匹配结果忽略捕获组例如:^\d+\s+(\d+)
要按预期获得捕获组匹配,您需要类似 (Swift4) 的东西:
public extension String {
public func capturedGroups(withRegex pattern: String) -> [String] {
var results = [String]()
var regex: NSRegularExpression
do {
regex = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return results
}
let matches = regex.matches(in: self, options: [], range: NSRange(location:0, length: self.count))
guard let match = matches.first else { return results }
let lastRangeIndex = match.numberOfRanges - 1
guard lastRangeIndex >= 1 else { return results }
for i in 1...lastRangeIndex {
let capturedGroupIndex = match.range(at: i)
let matchedString = (self as NSString).substring(with: capturedGroupIndex)
results.append(matchedString)
}
return results
}
}
非常感谢 Lars Blumberg his 与 Swift 4 进行分组和完整匹配,这对我帮助很大。我还为那些在正则表达式无效时确实想要 error.localizedDescription 响应的人添加了内容:
extension String {
func matchingStrings(regex: String) -> [[String]] {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.range(at: [=10=]).location != NSNotFound
? nsString.substring(with: result.range(at: [=10=]))
: ""
}
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
对我来说,将 localizedDescription 作为错误有助于理解转义出了什么问题,因为它显示了最终正则表达式 swift 试图实现的内容。
Swift 4 没有 NSString。
extension String {
func matches(regex: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
let matches = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count))
return matches.map { match in
return String(self[Range(match.range, in: self)!])
}
}
}
return 中所有匹配项和捕获组的最快方法 Swift 5
extension String {
func match(_ regex: String) -> [[String]] {
let nsString = self as NSString
return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)).map { match in
(0..<match.numberOfRanges).map { match.range(at: [=10=]).location == NSNotFound ? "" : nsString.substring(with: match.range(at: [=10=])) }
} ?? []
}
}
Returns 二维字符串数组:
"prefix12suffix fix1su".match("fix([0-9]+)su")
returns...
[["fix12su", "12"], ["fix1su", "1"]]
// First element of sub-array is the match
// All subsequent elements are the capture groups
将@Mike Chirico 更新为 Swift 5
extension String{
func regex(pattern: String) -> [String]?{
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options(rawValue: 0))
let all = NSRange(location: 0, length: count)
var matches = [String]()
regex.enumerateMatches(in: self, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: all) {
(result : NSTextCheckingResult?, _, _) in
if let r = result {
let nsstr = self as NSString
let result = nsstr.substring(with: r.range) as String
matches.append(result)
}
}
return matches
} catch {
return nil
}
}
}
基本phone号码匹配
let phoneNumbers = ["+79990001101", "+7 (800) 000-11-02", "+34 507 574 147 ", "+1-202-555-0118"]
let match: (String) -> String = {
[=10=].replacingOccurrences(of: #"[^\d+]"#, with: "", options: .regularExpression)
}
print(phoneNumbers.map(match))
// ["+79990001101", "+78000001102", "+34507574147", "+12025550118"]
我想从匹配正则表达式模式的字符串中提取子字符串。
所以我正在寻找这样的东西:
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
???
}
这就是我所拥有的:
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
var regex = NSRegularExpression(pattern: regex,
options: nil, error: nil)
var results = regex.matchesInString(text,
options: nil, range: NSMakeRange(0, countElements(text)))
as Array<NSTextCheckingResult>
/// ???
return ...
}
问题是,matchesInString
为我提供了一个 NSTextCheckingResult
的数组,其中 NSTextCheckingResult.range
的类型为 NSRange
。
NSRange
与 Range<String.Index>
不兼容,所以它阻止我使用 text.substringWithRange(...)
知道如何在 swift 中实现这个简单的事情而不需要太多代码行吗?
即使 matchesInString()
方法将 String
作为第一个参数,
它在内部与 NSString
一起工作,并且必须给出范围参数
使用 NSString
长度而不是 Swift 字符串长度。否则会
"extended grapheme clusters" 失败,例如 "flags".
自 Swift 4 (Xcode 9) 起,Swift 标准
库提供了在 Range<String.Index>
之间进行转换的函数
和 NSRange
.
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
String(text[Range([=10=].range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
示例:
let string = "€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
注意:强制展开Range([=23=].range, in: text)!
是安全的,因为
NSRange
指的是给定字符串 text
的子字符串。
但是,如果您想避免它,请使用
return results.flatMap {
Range([=12=].range, in: text).map { String(text[[=12=]]) }
}
相反。
(Swift 3 及更早版本的旧答案:)
所以你应该将给定的 Swift 字符串转换为 NSString
然后提取
范围。结果将自动转换为 Swift 字符串数组。
(Swift 1.2 的代码可以在编辑历史中找到。)
Swift 2 (Xcode 7.3.1) :
func matchesForRegexInText(regex: String, text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text,
options: [], range: NSMakeRange(0, nsString.length))
return results.map { nsString.substringWithRange([=13=].range)}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
示例:
let string = "€4€9"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]
Swift 3 (Xcode 8)
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: [=15=].range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
示例:
let string = "€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
我就是这样做的,我希望它能为 Swift.
带来新的视角在下面的这个例子中,我将得到 []
var sample = "this is an [hello] amazing [world]"
var regex = NSRegularExpression(pattern: "\[.+?\]"
, options: NSRegularExpressionOptions.CaseInsensitive
, error: nil)
var matches = regex?.matchesInString(sample, options: nil
, range: NSMakeRange(0, countElements(sample))) as Array<NSTextCheckingResult>
for match in matches {
let r = (sample as NSString).substringWithRange(match.range)//cast to NSString is required to match range format.
println("found= \(r)")
}
如果您想从字符串中提取子字符串,而不仅仅是位置(而是包含表情符号的实际字符串)。那么,以下可能是一个更简单的解决方案。
extension String {
func regex (pattern: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))
let nsstr = self as NSString
let all = NSRange(location: 0, length: nsstr.length)
var matches : [String] = [String]()
regex.enumerateMatchesInString(self, options: NSMatchingOptions(rawValue: 0), range: all) {
(result : NSTextCheckingResult?, _, _) in
if let r = result {
let result = nsstr.substringWithRange(r.range) as String
matches.append(result)
}
}
return matches
} catch {
return [String]()
}
}
}
用法示例:
"someText ⚽️ pig".regex("⚽️")
将return以下内容:
["⚽️"]
注意使用“\w+”可能会产生意外的“”
"someText ⚽️ pig".regex("\w+")
请问return这个String数组
["someText", "️", "pig"]
@p4bloch 如果你想从一系列捕获括号中捕获结果,那么你需要使用NSTextCheckingResult
的rangeAtIndex(index)
方法,而不是range
。这是来自上面的 Swift2 @MartinR 的方法,适用于捕获括号。在返回的数组中,第一个结果 [0]
是整个捕获,然后各个捕获组从 [1]
开始。我注释掉了 map
操作(这样更容易看到我更改了什么)并将其替换为嵌套循环。
func matches(for regex: String!, in text: String!) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text, options: [], range: NSMakeRange(0, nsString.length))
var match = [String]()
for result in results {
for i in 0..<result.numberOfRanges {
match.append(nsString.substringWithRange( result.rangeAtIndex(i) ))
}
}
return match
//return results.map { nsString.substringWithRange( [=10=].range )} //rangeAtIndex(0)
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
一个示例用例可能是,假设您想要拆分 title year
的字符串,例如 "Finding Dory 2016" 您可以这样做:
print ( matches(for: "^(.+)\s(\d{4})" , in: "Finding Dory 2016"))
// ["Finding Dory 2016", "Finding Dory", "2016"]
我的答案建立在给定答案的基础上,但通过添加额外的支持使正则表达式匹配更加稳健:
- Returns 不仅匹配,而且 returns 还匹配所有捕获组 (见下面的示例)
- 此解决方案支持可选匹配 ,而不是返回空数组
- 通过不打印到控制台来避免
do/catch
并且 使用guard
构造 - 将
matchingStrings
作为 扩展添加到String
Swift 4.2
//: Playground - noun: a place where people can play
import Foundation
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.range(at: [=10=]).location != NSNotFound
? nsString.substring(with: result.range(at: [=10=]))
: ""
}
}
}
}
"prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])")
// Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]]
"prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["prefix12", "12"]]
"12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["12", "12"]], other answers return an empty array here
// Safely accessing the capture of the first match (if any):
let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1]
// Prints: Optional("12")
Swift 3
//: Playground - noun: a place where people can play
import Foundation
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.rangeAt([=11=]).location != NSNotFound
? nsString.substring(with: result.rangeAt([=11=]))
: ""
}
}
}
}
"prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])")
// Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]]
"prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["prefix12", "12"]]
"12".matchingStrings(regex: "(?:prefix)?([0-9]+)")
// Prints: [["12", "12"]], other answers return an empty array here
// Safely accessing the capture of the first match (if any):
let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1]
// Prints: Optional("12")
Swift 2
extension String {
func matchingStrings(regex: String) -> [[String]] {
guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] }
let nsString = self as NSString
let results = regex.matchesInString(self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.rangeAtIndex([=12=]).location != NSNotFound
? nsString.substringWithRange(result.rangeAtIndex([=12=]))
: ""
}
}
}
}
不幸的是,我发现已接受的答案的解决方案无法在 Swift 3 for Linux 上编译。这是修改后的版本:
import Foundation
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try RegularExpression(pattern: regex, options: [])
let nsString = NSString(string: text)
let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: [=10=].range) }
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
主要区别是:
Swift on Linux 似乎需要在没有 Swift-native 等价物的 Foundation 对象上删除
NS
前缀。 (参见 Swift evolution proposal #86。)Swift on Linux 还需要为
RegularExpression
初始化和matches
方法指定options
参数。出于某种原因,将
String
强制转换为NSString
在 Swift 和 Linux 中不起作用,但初始化一个新的NSString
与String
因为来源确实有效。
此版本也适用于 macOS 上的 Swift 3 / Xcode,唯一的例外是您必须使用名称 NSRegularExpression
而不是 RegularExpression
.
这是一个非常简单的解决方案,returns 一个包含匹配项的字符串数组
Swift 3.
internal func stringsMatching(regularExpressionPattern: String, options: NSRegularExpression.Options = []) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regularExpressionPattern, options: options) else {
return []
}
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map {
nsString.substring(with: [=10=].range)
}
}
上面的大多数解决方案只给出完全匹配结果忽略捕获组例如:^\d+\s+(\d+)
要按预期获得捕获组匹配,您需要类似 (Swift4) 的东西:
public extension String {
public func capturedGroups(withRegex pattern: String) -> [String] {
var results = [String]()
var regex: NSRegularExpression
do {
regex = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return results
}
let matches = regex.matches(in: self, options: [], range: NSRange(location:0, length: self.count))
guard let match = matches.first else { return results }
let lastRangeIndex = match.numberOfRanges - 1
guard lastRangeIndex >= 1 else { return results }
for i in 1...lastRangeIndex {
let capturedGroupIndex = match.range(at: i)
let matchedString = (self as NSString).substring(with: capturedGroupIndex)
results.append(matchedString)
}
return results
}
}
非常感谢 Lars Blumberg his
extension String {
func matchingStrings(regex: String) -> [[String]] {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = self as NSString
let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
return results.map { result in
(0..<result.numberOfRanges).map {
result.range(at: [=10=]).location != NSNotFound
? nsString.substring(with: result.range(at: [=10=]))
: ""
}
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
对我来说,将 localizedDescription 作为错误有助于理解转义出了什么问题,因为它显示了最终正则表达式 swift 试图实现的内容。
Swift 4 没有 NSString。
extension String {
func matches(regex: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
let matches = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count))
return matches.map { match in
return String(self[Range(match.range, in: self)!])
}
}
}
return 中所有匹配项和捕获组的最快方法 Swift 5
extension String {
func match(_ regex: String) -> [[String]] {
let nsString = self as NSString
return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)).map { match in
(0..<match.numberOfRanges).map { match.range(at: [=10=]).location == NSNotFound ? "" : nsString.substring(with: match.range(at: [=10=])) }
} ?? []
}
}
Returns 二维字符串数组:
"prefix12suffix fix1su".match("fix([0-9]+)su")
returns...
[["fix12su", "12"], ["fix1su", "1"]]
// First element of sub-array is the match
// All subsequent elements are the capture groups
将@Mike Chirico 更新为 Swift 5
extension String{
func regex(pattern: String) -> [String]?{
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options(rawValue: 0))
let all = NSRange(location: 0, length: count)
var matches = [String]()
regex.enumerateMatches(in: self, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: all) {
(result : NSTextCheckingResult?, _, _) in
if let r = result {
let nsstr = self as NSString
let result = nsstr.substring(with: r.range) as String
matches.append(result)
}
}
return matches
} catch {
return nil
}
}
}
基本phone号码匹配
let phoneNumbers = ["+79990001101", "+7 (800) 000-11-02", "+34 507 574 147 ", "+1-202-555-0118"]
let match: (String) -> String = {
[=10=].replacingOccurrences(of: #"[^\d+]"#, with: "", options: .regularExpression)
}
print(phoneNumbers.map(match))
// ["+79990001101", "+78000001102", "+34507574147", "+12025550118"]