ios Swift 提取正则表达式匹配
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27880650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift extract regex matches
提问by mitchkman
I want to extract substrings from a string that match a regex pattern.
我想从匹配正则表达式模式的字符串中提取子字符串。
So I'm looking for something like this:
所以我正在寻找这样的东西:
func matchesForRegexInText(regex: String!, text: String!) -> [String] {
???
}
So this is what I have:
所以这就是我所拥有的:
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 ...
}
The problem is, that matchesInString
delivers me an array of NSTextCheckingResult
, where NSTextCheckingResult.range
is of type NSRange
.
问题是,它matchesInString
为我提供了一个NSTextCheckingResult
,其中NSTextCheckingResult.range
类型为 的数组NSRange
。
NSRange
is incompatible with Range<String.Index>
, so it prevents me of using text.substringWithRange(...)
NSRange
与 不兼容Range<String.Index>
,因此它阻止我使用text.substringWithRange(...)
Any idea how to achieve this simple thing in swift without too many lines of code?
知道如何在没有太多代码行的情况下快速实现这个简单的事情吗?
回答by Martin R
Even if the matchesInString()
method takes a String
as the first argument,
it works internally with NSString
, and the range parameter must be given
using the NSString
length and not as the Swift string length. Otherwise it will
fail for "extended grapheme clusters" such as "flags".
即使该matchesInString()
方法将 aString
作为第一个参数,它也可以在内部使用NSString
,并且必须使用NSString
长度而不是 Swift 字符串长度来指定范围参数。否则对于“扩展的字素簇”,例如“标志”,它将失败。
As of Swift 4(Xcode 9), the Swift standard
library provides functions to convert between Range<String.Index>
and NSRange
.
从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(let string = "49"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
.range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
Example:
例子:
return results.flatMap {
Range(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(let string = "49"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]
.range)}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
.range, in: text).map { String(text[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: let string = "49"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]
.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
]) }
}
Note:The forced unwrap Range($0.range, in: text)!
is safe because
the NSRange
refers to a substring of the given string text
.
However, if you want to avoid it then use
注意:强制解包Range($0.range, in: text)!
是安全的,因为NSRange
指的是给定 string 的子字符串text
。但是,如果您想避免它,请使用
//: 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: //: 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(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(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]()
}
}
}
).location != NSNotFound
? nsString.substringWithRange(result.rangeAtIndex("someText ?? pig".regex("??")
))
: ""
}
}
}
}
).location != NSNotFound
? nsString.substring(with: result.rangeAt(["??"]
))
: ""
}
}
}
}
"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")
).location != NSNotFound
? nsString.substring(with: result.range(at: "someText ?? pig".regex("\w+")
))
: ""
}
}
}
}
"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")
instead.
反而。
(Older answer for Swift 3 and earlier:)
(Swift 3 及更早版本的旧答案:)
So you should convert the given Swift string to an NSString
and then extract the
ranges. The result will be converted to a Swift string array automatically.
因此,您应该将给定的 Swift 字符串转换为 an NSString
,然后提取范围。结果将自动转换为 Swift 字符串数组。
(The code for Swift 1.2 can be found in the edit history.)
(Swift 1.2 的代码可以在编辑历史中找到。)
Swift 2 (Xcode 7.3.1) :
斯威夫特 2(Xcode 7.3.1):
["someText", "?", "pig"]
Example:
例子:
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: 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( print ( matches(for: "^(.+)\s(\d{4})" , in: "Finding Dory 2016"))
// ["Finding Dory 2016", "Finding Dory", "2016"]
.range )} //rangeAtIndex(0)
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
.range) }
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
Swift 3 (Xcode 8)
斯威夫特 3 (Xcode 8)
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
}
}
Example:
例子:
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: 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)")
}
.range)
}
}
回答by Lars Blumberg
My answer builds on top of given answers but makes regex matching more robust by adding additional support:
我的答案建立在给定的答案之上,但通过添加额外的支持使正则表达式匹配更加强大:
- Returns not only matches but returns also all capturing groupsfor each match (see examples below)
- Instead of returning an empty array, this solution supports optional matches
- Avoids
do/catch
by not printing to the console and makes use of theguard
construct - Adds
matchingStrings
as an extension toString
- 不仅返回匹配项,还返回每个匹配项的所有捕获组(参见下面的示例)
- 此解决方案不返回空数组,而是支持可选匹配
do/catch
通过不打印到控制台来避免并使用guard
构造matchingStrings
作为扩展添加到String
Swift 4.2
斯威夫特 4.2
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)!])
}
}
}
Swift 3
斯威夫特 3
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: "prefix12suffix fix1su".match("fix([0-9]+)su")
).location == NSNotFound ? "" : nsString.substring(with: match.range(at: [["fix12su", "12"], ["fix1su", "1"]]
// First element of sub-array is the match
// All subsequent elements are the capture groups
)) }
} ?? []
}
}
Swift 2
斯威夫特 2
##代码##回答by Mike Chirico
If you want to extract substrings from a String, not just the position, (but the actual String including emojis). Then, the following maybe a simpler solution.
如果您想从字符串中提取子字符串,不仅是位置,(而是包含表情符号的实际字符串)。那么,以下可能是一个更简单的解决方案。
##代码##Example Usage:
示例用法:
##代码##Will return the following:
将返回以下内容:
##代码##Note using "\w+" may produce an unexpected ""
注意使用 "\w+" 可能会产生意外的 ""
##代码##Will return this String array
将返回这个字符串数组
##代码##回答by Rob Mecham
I found that the accepted answer's solution unfortunately does not compile on Swift 3 for Linux. Here's a modified version, then, that does:
不幸的是,我发现接受的答案的解决方案无法在 Swift 3 for Linux 上编译。这是一个修改后的版本,然后:
##代码##The main differences are:
主要区别是:
Swift on Linux seems to require dropping the
NS
prefix on Foundation objects for which there is no Swift-native equivalent. (See Swift evolution proposal #86.)Swift on Linux also requires specifying the
options
arguments for both theRegularExpression
initialization and thematches
method.For some reason, coercing a
String
into anNSString
doesn't work in Swift on Linux but initializing a newNSString
with aString
as the source does work.
Linux 上的 Swift 似乎需要删除
NS
Foundation 对象上没有 Swift 原生等效项的前缀。(参见Swift 进化提案 #86。)Linux 上的 Swift 还需要
options
为RegularExpression
初始化和matches
方法指定参数。出于某种原因,将 a 强制
String
转换为 anNSString
在 Linux 上的 Swift 中不起作用,但在源代码中NSString
使用 a初始化 newString
确实有效。
This version also works with Swift 3 on macOS / Xcode with the sole exception that you must use the name NSRegularExpression
instead of RegularExpression
.
此版本也适用于 macOS / Xcode 上的 Swift 3,唯一的例外是您必须使用名称NSRegularExpression
而不是RegularExpression
.
回答by OliverD
@p4bloch if you want to capture results from a series of capture parentheses, then you need to use the rangeAtIndex(index)
method of NSTextCheckingResult
, instead of range
. Here's @MartinR 's method for Swift2 from above, adapted for capture parentheses. In the array that is returned, the first result [0]
is the entire capture, and then individual capture groups begin from [1]
. I commented out the map
operation (so it's easier to see what I changed) and replaced it with nested loops.
@p4bloch 如果要从一系列捕获括号中捕获结果,则需要使用, 而不是的rangeAtIndex(index)
方法。这是@MartinR 上面用于 Swift2 的方法,适用于捕获括号。在返回的数组中,第一个结果是整个捕获,然后各个捕获组从 开始。我注释掉了操作(这样更容易看到我更改的内容)并用嵌套循环替换它。NSTextCheckingResult
range
[0]
[1]
map
An example use case might be, say you want to split a string of title year
eg "Finding Dory 2016" you could do this:
一个示例用例可能是,假设您想拆分一个字符串,title year
例如“Finding Dory 2016”,您可以这样做:
回答by valexa
Most of the solutions above only give the full match as a result ignoring the capture groups e.g.: ^\d+\s+(\d+)
上面的大多数解决方案只给出完全匹配,因此忽略了捕获组,例如:^\d+\s+(\d+)
To get the capture group matches as expected you need something like (Swift4) :
要按预期获得捕获组匹配,您需要类似 (Swift4) 的内容:
##代码##回答by Jorge Osorio
This is a very simple solution that returns an array of string with the matches
这是一个非常简单的解决方案,它返回一个包含匹配项的字符串数组
Swift 3.
斯威夫特 3.
##代码##回答by Dalorzo
This is how I did it, I hope it brings a new perspective how this works on Swift.
我就是这样做的,我希望它能带来一个新的视角,它是如何在 Swift 上工作的。
In this example below I will get the any string between []
在下面的这个例子中,我将得到之间的任何字符串 []
回答by shiami
Swift 4 without NSString.
没有 NSString 的 Swift 4。
##代码##回答by Ken Mueller
The fastest way to return all matches and capture groups in Swift 5
在 Swift 5 中返回所有匹配项和捕获组的最快方法
##代码##Returns a 2-dimentional array of strings:
返回一个二维字符串数组:
##代码##returns...
返回...
##代码##