我想从匹配正则表达式模式的字符串中提取子字符串。
所以我正在寻找这样的东西:
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中实现这个简单的事情?
答案 0 :(得分:274)
即使matchesInString()
方法将String
作为第一个参数,
它在内部使用NSString
,并且必须给出范围参数
使用NSString
长度而不是Swift字符串长度。否则它会
“标志”等“扩展的字形集群”失败。
截至 Swift 4 (Xcode 9),Swift标准
library提供了在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($0.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($0.range, in: text)!
是安全的,因为
NSRange
引用给定字符串text
的子字符串。
但是,如果你想避免它,那么使用
return results.flatMap {
Range($0.range, in: text).map { String(text[$0]) }
}
代替。
(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($0.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: $0.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"]
答案 1 :(得分:51)
我的答案建立在给定答案之上,但通过添加额外支持使正则表达式匹配更加强大:
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: $0).location != NSNotFound
? nsString.substring(with: result.range(at: $0))
: ""
}
}
}
}
"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($0).location != NSNotFound
? nsString.substring(with: result.rangeAt($0))
: ""
}
}
}
}
"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($0).location != NSNotFound
? nsString.substringWithRange(result.rangeAtIndex($0))
: ""
}
}
}
}
答案 2 :(得分:11)
如果要从String中提取子串,而不仅仅是位置,(但实际的String包括emojis)。然后,以下可能是一个更简单的解决方案。
$("#mydiv").load("scripts/loadimage.php",function(){
$(".html5lightbox").html5lightbox();
});
示例用法:
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("⚽️")
注意使用“\ w +”可能会产生意外的“”
["⚽️"]
将返回此String数组
"someText ⚽️ pig".regex("\\w+")
答案 3 :(得分:9)
我发现,接受答案的解决方案很遗憾无法在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: $0.range) }
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
主要区别是:
Linux上的Swift似乎要求删除没有Swift原生等效的Foundation对象的NS
前缀。 (见Swift evolution proposal #86。)
Linux上的Swift还需要为options
初始化和RegularExpression
方法指定matches
个参数。
由于某些原因,将String
强制转换为NSString
并不适用于Linux上的Swift,但使用NSString
初始化新的String
因为来源确实有效。
此版本也适用于macOS / Xcode上的Swift 3,唯一的例外是您必须使用名称NSRegularExpression
而不是RegularExpression
。
答案 4 :(得分:5)
@ p4bloch如果要捕获一系列捕获括号的结果,则需要使用rangeAtIndex(index)
的{{1}}方法,而不是NSTextCheckingResult
。这是@MartinR从上面开始的Swift2方法,适用于捕获括号。在返回的数组中,第一个结果range
是整个捕获,然后各个捕获组从[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( $0.range )} //rangeAtIndex(0)
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
字符串,例如“Finding Dory 2016”,您可以这样做:
title year
答案 5 :(得分:3)
上面的大多数解决方案只提供完全匹配,因此忽略了捕获组,例如:^ \ 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
}
}
答案 6 :(得分:2)
我就是这样做的,我希望它能为Swift带来一个新的视角。
在下面的示例中,我将获得[a-zA-Z]+(Field|Type)[0-9]{1,2}
[]
答案 7 :(得分:2)
这是一个非常简单的解决方案,它返回带有匹配项
的字符串数组 斯威夫特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: $0.range)
}
}
答案 8 :(得分:0)
非常感谢Lars Blumberg他的answer用 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: $0).location != NSNotFound
? nsString.substring(with: result.range(at: $0))
: ""
}
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
对我来说,将localizedDescription设置为错误有助于理解转义出了什么问题,因为它显示了最终正则表达式swift试图实现的结果。
答案 9 :(得分:0)
不带NSString的Swift 4。
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)!])
}
}
}
答案 10 :(得分:0)
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, count)).map { match in
(0..<match.numberOfRanges).map { match.range(at: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) }
} ?? []
}
}
返回二维数组的字符串:
"prefix12suffix fix1su".match("fix([0-9]+)su")
返回...
[["fix12su", "12"], ["fix1su", "1"]]
// First element of sub-array is the match
// All subsequent elements are the capture groups
答案 11 :(得分:0)
将@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
}
}
}