Apple称NSRegularExpression基于ICU正则表达式库:https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSRegularExpression_Class/
目前支持的模式语法是ICU指定的语法。 ICU正则表达式在http://userguide.icu-project.org/strings/regexp描述。
该页面(在icu-project.org上)声称现在支持命名捕获组,使用与.NET Regular Expressions相同的语法:
(?<name>...)
命名捕获组。<angle brackets>
是文字的 - 它们出现在模式中。
我编写了一个程序,它可以获得一个看似正确的多个范围的匹配 - 尽管每个范围都返回两次(原因未知) - 但我唯一的信息是范围索引及其文本范围
例如,正则表达式:^(?<foo>foo)\.(?<bar>bar)\.(?<bar2>baz)$
包含测试字符串foo.bar.baz
给我这些结果:
Idx Start Length Text
0 0 11 foo.bar.baz
1 0 3 foo
2 4 3 bar
3 8 3 baz
有没有办法知道&#34; baz
&#34;来自捕获组bar2
?
答案 0 :(得分:4)
由于支持iOS11命名的捕获组。 NSTextCheckingResult
具有open func range(withName name: String) -> NSRange
函数。
使用带有测试字符串^(?<foo>foo)\.(?<bar>bar)\.(?<bar2>baz)$
的正则表达式:foo.bar.baz
可得到4个结果匹配项。函数match.range(withName: "bar2")
返回字符串baz
答案 1 :(得分:1)
我遇到了同样的问题,最终支持我自己的解决方案。随意评论或改进; - )
extension NSRegularExpression {
typealias GroupNamesSearchResult = (NSTextCheckingResult, NSTextCheckingResult, Int)
private func textCheckingResultsOfNamedCaptureGroups() throws -> [String:GroupNamesSearchResult] {
var groupnames = [String:GroupNamesSearchResult]()
let greg = try NSRegularExpression(pattern: "^\\(\\?<([\\w\\a_-]*)>.*\\)$", options: NSRegularExpressionOptions.DotMatchesLineSeparators)
let reg = try NSRegularExpression(pattern: "\\([^\\(\\)]*\\)", options: NSRegularExpressionOptions.DotMatchesLineSeparators)
let m = reg.matchesInString(self.pattern, options: NSMatchingOptions.WithTransparentBounds, range: NSRange(location: 0, length: self.pattern.utf16.count))
for (n,g) in m.enumerate() {
let gstring = self.pattern.substringWithRange(g.rangeAtIndex(0).toRange()!)
print(self.pattern.substringWithRange(g.rangeAtIndex(0).toRange()!))
let gmatch = greg.matchesInString(gstring, options: NSMatchingOptions.Anchored, range: NSRange(location: 0, length: gstring.utf16.count))
if gmatch.count > 0{
groupnames[gstring.substringWithRange(gmatch[0].rangeAtIndex(1).toRange()!)] = (g,gmatch[0],n)
}
}
return groupnames
}
func indexOfNamedCaptureGroups() throws -> [String:Int] {
var groupnames = [String:Int]()
for (name,(_,_,n)) in try self.textCheckingResultsOfNamedCaptureGroups() {
groupnames[name] = n + 1
}
//print(groupnames)
return groupnames
}
func rangesOfNamedCaptureGroups(match:NSTextCheckingResult) throws -> [String:Range<Int>] {
var ranges = [String:Range<Int>]()
for (name,(_,_,n)) in try self.textCheckingResultsOfNamedCaptureGroups() {
ranges[name] = match.rangeAtIndex(n+1).toRange()
}
return ranges
}
}
以下是一个用法示例:
let node = "'test_literal'"
let regex = try NSRegularExpression(pattern: "^(?<delimiter>'|\")(?<value>.*)(?:\\k<delimiter>)$", options: NSRegularExpressionOptions.DotMatchesLineSeparators)
let match = regex.matchesInString(node, options: NSMatchingOptions.Anchored, range: NSRange(location: 0,length: node.utf16.count))
if match.count > 0 {
let ranges = try regex.rangesOfNamedCaptureGroups(match[0])
guard let range = ranges["value"] else {
}
}
答案 2 :(得分:1)
我参与了由Daniele Bernardini创建的示例。
有许多变化:
我更喜欢实际接收Set中的实际捕获。我添加了一个名为captureGroups()
的方法,它将捕获作为字符串而不是范围返回。
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))
: ""
}
}
}
func range(from nsRange: NSRange) -> Range<String.Index>? {
guard
let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
let to16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location + nsRange.length, limitedBy: utf16.endIndex),
let from = from16.samePosition(in: self),
let to = to16.samePosition(in: self)
else { return nil }
return from ..< to
}
}
extension NSRegularExpression {
typealias GroupNamesSearchResult = (NSTextCheckingResult, NSTextCheckingResult, Int)
private func textCheckingResultsOfNamedCaptureGroups() -> [String:GroupNamesSearchResult] {
var groupnames = [String:GroupNamesSearchResult]()
guard let greg = try? NSRegularExpression(pattern: "^\\(\\?<([\\w\\a_-]*)>$", options: NSRegularExpression.Options.dotMatchesLineSeparators) else {
// This never happens but the alternative is to make this method throwing
return groupnames
}
guard let reg = try? NSRegularExpression(pattern: "\\(.*?>", options: NSRegularExpression.Options.dotMatchesLineSeparators) else {
// This never happens but the alternative is to make this method throwing
return groupnames
}
let m = reg.matches(in: self.pattern, options: NSRegularExpression.MatchingOptions.withTransparentBounds, range: NSRange(location: 0, length: self.pattern.utf16.count))
for (n,g) in m.enumerated() {
let r = self.pattern.range(from: g.rangeAt(0))
let gstring = self.pattern.substring(with: r!)
let gmatch = greg.matches(in: gstring, options: NSRegularExpression.MatchingOptions.anchored, range: NSRange(location: 0, length: gstring.utf16.count))
if gmatch.count > 0{
let r2 = gstring.range(from: gmatch[0].rangeAt(1))!
groupnames[gstring.substring(with: r2)] = (g, gmatch[0],n)
}
}
return groupnames
}
func indexOfNamedCaptureGroups() throws -> [String:Int] {
var groupnames = [String:Int]()
for (name,(_,_,n)) in try self.textCheckingResultsOfNamedCaptureGroups() {
groupnames[name] = n + 1
}
return groupnames
}
func rangesOfNamedCaptureGroups(match:NSTextCheckingResult) throws -> [String:Range<Int>] {
var ranges = [String:Range<Int>]()
for (name,(_,_,n)) in try self.textCheckingResultsOfNamedCaptureGroups() {
ranges[name] = match.rangeAt(n+1).toRange()
}
return ranges
}
private func nameForIndex(_ index: Int, from: [String:GroupNamesSearchResult]) -> String? {
for (name,(_,_,n)) in from {
if (n + 1) == index {
return name
}
}
return nil
}
func captureGroups(string: String, options: NSRegularExpression.MatchingOptions = []) -> [String:String] {
return captureGroups(string: string, options: options, range: NSRange(location: 0, length: string.utf16.count))
}
func captureGroups(string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> [String:String] {
var dict = [String:String]()
let matchResult = matches(in: string, options: options, range: range)
let names = try self.textCheckingResultsOfNamedCaptureGroups()
for (n,m) in matchResult.enumerated() {
for i in (0..<m.numberOfRanges) {
let r2 = string.range(from: m.rangeAt(i))!
let g = string.substring(with: r2)
if let name = nameForIndex(i, from: names) {
dict[name] = g
}
}
}
return dict
}
}
使用新方法captureGroups()
的一个示例是:
let node = "'test_literal'"
let regex = try NSRegularExpression(pattern: "^(?<all>(?<delimiter>'|\")(?<value>.*)(?:\\k<delimiter>))$", options: NSRegularExpression.Options.dotMatchesLineSeparators)
let match2 = regex.captureGroups(string: node, options: NSRegularExpression.MatchingOptions.anchored)
print(match2)
它会打印出来:
[“delimiter”:“\'”,“all”:“\'test_literal \'”,“value”:“test_literal”]