Ios NSRegularExpression中的命名捕获组-获取范围';s组';姓名
苹果称NSRegularExpression基于ICU正则表达式库: 当前支持的模式语法是由ICU指定的。有关ICU正则表达式的说明,请参阅 该页面(在icu project.org上)声称现在支持命名捕获组,使用与.NET正则表达式相同的语法:Ios NSRegularExpression中的命名捕获组-获取范围';s组';姓名,ios,regex,nsregularexpression,icu,Ios,Regex,Nsregularexpression,Icu,苹果称NSRegularExpression基于ICU正则表达式库: 当前支持的模式语法是由ICU指定的。有关ICU正则表达式的说明,请参阅 该页面(在icu project.org上)声称现在支持命名捕获组,使用与.NET正则表达式相同的语法: (?…)命名的捕获组。是文字-它们出现在模式中 我已经编写了一个程序,它可以获得一个与多个范围的匹配,这似乎是正确的-尽管每个范围返回两次(原因未知)-但我唯一掌握的信息是范围的索引及其文本范围 例如,正则表达式:^(?foo)\.(?bar)\.(?
(?…)
命名的捕获组。
是文字-它们出现在模式中
我已经编写了一个程序,它可以获得一个与多个范围的匹配,这似乎是正确的-尽管每个范围返回两次(原因未知)-但我唯一掌握的信息是范围的索引及其文本范围
例如,正则表达式:^(?foo)\.(?bar)\.(?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
有没有办法知道“
baz
”来自捕获组bar2
?我也面临同样的问题,最终支持了我自己的解决方案。请随意评论或改进;-)
扩展名NSRegularExpression{
typealias GroupNamesSearchResult=(NSTextCheckingResult,NSTextCheckingResult,Int)
private func textCheckingResultsOfNamedCaptureGroups()抛出->[字符串:GroupNamesSearchResult]{
var groupnames=[String:GroupNamesSearchResult]()
让greg=尝试NSRegularExpression(模式:“^\\(\\?.\\)$”,选项:NSRegularExpressionOptions.DotMatchesLineSeparators)
let reg=尝试NSRegularExpression(模式:“\\([^\\(\\)]*\\”,选项:NSRegularExpressionOptions.DotMatchesLineSeparators)
设m=reg.matchesInstalling(self.pattern,选项:NSMatchingOptions.WithTransparentBounds,范围:NSRange(位置:0,长度:self.pattern.utf16.count))
对于m.enumerate()中的(n,g){
让gstring=self.pattern.substringWithRange(g.rangeAtIndex(0.toRange()!)
打印(self.pattern.substringWithRange(g.rangeAtIndex(0.toRange()!))
设gmatch=greg.matcheInstalling(gstring,选项:NSMatchingOptions.archored,范围:NSRange(位置:0,长度:gstring.utf16.count))
如果gmatch.count>0{
GroupName[gstring.substringWithRange(gmatch[0].RangeAtinex(1).toRange()!)]=(g,gmatch[0],n)
}
}
返回组名
}
func indexOfNamedCaptureGroups()抛出->[字符串:Int]{
var groupnames=[String:Int]()
对于try self.textCheckingResultsOfNamedCaptureGroups()中的(name,(,,,n)){
组名[名称]=n+1
}
//打印(组名)
返回组名
}
func rangesOfNamedCaptureGroups(匹配:NSTextCheckingResult)抛出->[字符串:范围]{
变量范围=[字符串:范围]()
对于try self.textCheckingResultsOfNamedCaptureGroups()中的(name,(,,,n)){
ranges[name]=match.rangeAtIndex(n+1).toRange()
}
返回范围
}
}
下面是一个使用示例:
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 {
}
}
let node=“'test_literal'”
让regex=try NSRegularExpression(模式:“^(?“\”)(?:\\k)$”,选项:NSRegularExpressionOptions.DotMatchesLineSeparators)
让match=regex.matcheInstalling(节点,选项:NSMatchingOptions.archored,范围:NSRange(位置:0,长度:node.utf16.count))
如果match.count>0{
let ranges=尝试regex.rangesOfNamedCaptureGroups(匹配[0])
防护等级范围=范围[“值”]其他{
}
}
我已经完成了创建的示例
有许多变化:
- 首先,代码现在与Swift 3兼容
- Daniele的代码有一个缺陷,它不会捕获嵌套的捕获。我已经使正则表达式稍微不那么激进,以允许捕获组嵌套
- 我更喜欢在一个集合中实际接收捕获。我添加了一个名为
的方法,该方法将捕获作为字符串而不是范围返回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 } }
[“分隔符”:“\”,“全部”:“\'test\u literal\”,“值”:“test\u literal”]因为支持iOS11命名捕获组。
具有此功能NSTextCheckingResult
将正则表达式:
与测试字符串^(?foo)\.(?bar)\.(?baz)$
一起使用,将给出4个结果匹配。函数foo.bar.baz
返回字符串的范围match.range(名称为:“bar2”)
您查看了吗?@Thomas我确实看到了,但是从2014年开始,讨论都说不支持命名捕获组-但是(至少在iOS 9和OS X 10.11上)do似乎是受支持的-而且他们在我的机器上工作,至少,我无法从结果范围映射回他们来自的组。而iOS 4.0和更高版本中可用的Apple doc状态…@Thomas文档指出,baz
类是添加到iOS 4.0的,而不是对命名Captur的支持e组是在iOS 4.0中添加的。@Thomas事实上,苹果自己的NSRegularExpression
文档中没有列出命名捕获组的语法,它只出现在ICU自己的文档中,表明命名捕获组是最近添加的。很有趣!您能用一些用法示例修改您的答案吗?完成。在ta之后再看一眼,我也意识到这并不明显……几个月前我写了这段代码,有理由在两个单独的调用中进行匹配和范围。谢谢,你救了我。我基于此编写了一个扩展,用所有命名的捕获组及其值创建一个字典:NSRegularExpression
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 } }
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)