[译] 翻译NSHipster,NSRegular​Expression 正则表达式

2017-09-13 10:25:56来源:segmentfault作者:邓轻舟人点击

分享

有些人说: 遇到问题,明白了。我要用 NSRegular​Expression: 正则表达式, 现在有三个问题


编程世界里, 正则表达式应用广泛。 有些觉得这个十分难以理解, 符号密集。 更多的程序员觉得,正则表达式是代码的基础 ,是一个笑话。
有些程序员觉得,正则表达式,真是轻便,有强大。没有工具库这样通用的神器的人,不知道他们怎么工作的。
首先,我们都认为,Cocoa 在NSRegularExpression中,封装了你不曾见识过的 强大的正则表达式。
不行吗?我们从这个HTML片段中提取链接,先用Ruby


htmlSource = "Questions? Corrections? [@NSHipster](http://www.jianshu.com/%22https://twitter.com/NSHipster/%22) or [on GitHub](http://www.jianshu.com/%22https://github.com/NSHipster/articles/%22)."
linkRegex = /]*href="([^"]*)"[^>]*>/i links = htmlSource.scan(linkRegex) puts(links)
# https://twitter.com/NSHipster
# https://github.com/NSHipster/articles

两三行程序,看你怎么数了。
还行吧,现在试试在Swift 中使用 NSRegularExpression 提取链接


lethtmlSource="Questions? Corrections? [@NSHipster](http://www.jianshu.com/%22https://twitter.com/NSHipster/%22) or [on GitHub](http://www.jianshu.com/%22https://github.com/NSHipster/articles/%22)."let linkRegexPattern = "]*href=/"([^/"]*)/"[^>]*>"letlinkRegex = try!NSRegularExpression(pattern: linkRegexPattern , options:.caseInsensitive)
letmatches = linkRegex.matches(in:htmlSource,range:NSMakeRange(0,htmlSource.utf16.count))
let links = matches.map{result->Stringing
let hrefRange = result.rangeAt(1)
let start = String.UTF16Index(hrefRange.location)
let end = String.UTF16Index(hrefRange.location+hrefRange.length)
return String(htmlSource.utf16[start..<end])!
}
print(links)
// ["https://twitter.com/NSHipster", "https://github.com/NSHipster/articles"]

不要BS


这篇文章,不会深入浅出地讲解正则表达式本身。你可能需要学习通配符,反向引用 ,先行断言等等。
阅读苹果的NSRegularExpression, NSTextCheckingResult及其相关联的文档,你会明了Swift 中的正则表达式。


NSString Methods , NSString 方法


在 Cocoa 框架中对正则表达式最简单的使用,当然是 跳过NSRegularExpression。
NSString 的 range(of:...) 方法,给出.regularExpression 选项时,即可切换到正则表达式模式,
然后 轻量级的搜索可以这么写:


Swift


let source = "For NSSet and NSDictionary, the breaking..."// 匹配一切Cocoa 中的类型
// UIButton, NSCharacterSet, NSURLSession, 等等
let typePattern = "[A-Z]{3,}[A-Za-z0-9]+"if let typeRange = source.range(of: typePattern,
options: .regularExpression) {
print("First type: /(source[typeRange])")
//NSSet
}

Objective-C


NSString *source = @"For NSSet and NSDictionary, the breaking...";// 匹配一切Cocoa 中的类型
// UIButton, NSCharacterSet, NSURLSession, 等等
NSString *typePattern = @"[A-Z]{3,}[A-Za-z0-9]+";
NSRange typeRange = [source rangeOfString:typePattern
options:NSRegularExpressionSearch];if (typeRange.location != NSNotFound) {
NSLog(@"First type: %@", [source substringWithRange:typeRange]);
// NSSet
}

同样的选项,使用replacingOccurrences(of:with:...)方法,轻松实现替换,
看,我们怎样通过这种巧妙的方法,在文本中用Markdown风格的倒引号,把每一个类型名括起来。


Swift


let markedUpSource = source.replacingOccurrences(of: typePattern,
with: "`$0`", options: .regularExpression)
print(markedUpSource)
//例句: "For `NSSet` and `NSDictionary`, the breaking...""

Objective-C


NSString *markedUpSource =
[source stringByReplacingOccurrencesOfString:typePattern
withString:@"`$0`"
options:NSRegularExpressionSearch
range:NSMakeRange(0, source.length)];
NSLog(@"%@", markedUpSource);
// 例句: "For `NSSet` and `NSDictionary`, the breaking...""

在替换模版中,这种运用正则表达式的途径,甚至可以处理小群的引用。
变形出又快又黑的儿童黑话


Swift


let ourcesay = source.replacingOccurrences(
of: "([bcdfghjklmnpqrstvwxyz]*)([a-z]+)",
with: "$2$1ay",
options: [.regularExpression, .caseInsensitive])
print(ourcesay)
// "orFay etNSSay anday ictionaryNSDay, ethay eakingbray..." 故意颠倒英语字母顺序拼凑而成的行话;儿童黑话

Objective-C


NSString *ourcesay =
[source stringByReplacingOccurrencesOfString:@"([bcdfghjklmnpqrstvwxyz]*)([a-z]+)"
withString:@"$2$1ay"
options:NSRegularExpressionSearch | NSCaseInsensitiveSearch
range:NSMakeRange(0, source.length)];
NSLog(@"%@", ourcesay);
// "orFay etNSSay anday ictionaryNSDay, ethay eakingbray..."故意颠倒英语字母顺序拼凑而成的行话;儿童黑话

这两个方法用来处理许多需要使用正则表达式的情况,足够了。我们需要NSRegularExpression本身来处理,更复杂的情况。
我们先用Swift 区分较复杂的数据。


NSRange and Swift , Swift 中的 NSRange


不同于Foundation框架下的NSString,Swift 字符串的字符与子串,更同意理解,看起来更复合。
Swift的标准库String的数据有 四种不同的编码方式
可以快速地通过字符、 统一码标量值、UTF-8 编码单元 或者 UTF-16编码单元 ,访问字符串里面的数据。
这与NSRegularExpression,有什么关系?
许多 NSRegularExpression 方法使用NSRange , NSTextCheckingResult的实例,来存取匹配的数据。
同样的, NSRange 使用两个整型参数 location 和 length,
但是String中没有使用整型参数作为索引的方式。


let range = NSRange(location: 4, length: 5)// 下面没有一种,能编译成功
source[range]
source.characters[range]
source.substring(with: range)
source.substring(with: range.toRange()!)

困惑吧! 心累吧!


继续干!


一切都不是,表面看起来毫无关联的。
Swift 中的 String, 明确就是 与Foundation框架下的 NSString 的APIs ,具有互操作性。
只要引入了Foundation框架,就可以直接以utf16的形式,直接用整型创建新的索引。


let start = String.UTF16Index(range.location)
let end = String.UTF16Index(range.location + range.length)
let substring = String(source.utf16[start..<end])!
// 现在子串是 "NSSet"

请记住, 对String扩展一下,就能轻松地区分开Swift、 Objective-C的 NSRange


extension String {
/// 有一个 `NSRange`反射出这个字符串的全部范围
var nsrange: NSRange {
return NSRange(location: 0, length: utf16.count)
} ///返回给定范围的子串
/// 如果该范围,转换失败,返回nil
func substring(with nsrange: NSRange) -> String? {
guard let range = nsrange.toRange()
else { return nil }
let start = UTF16Index(range.lowerBound)
let end = UTF16Index(range.upperBound)
return String(utf16[start..<end])
} /// 返回与 给定范围相同的范围
/// 如果该范围,转换失败,返回nil
func range(from nsrange: NSRange) -> Range<Index>? {
guard let range = nsrange.toRange() else { return nil }
let utf16Start = UTF16Index(range.lowerBound)
let utf16End = UTF16Index(range.upperBound) guard let start = Index(utf16Start, within: self),
let end = Index(utf16End, within: self)
else { return nil } return start..<end
}
}

下一结中,我们将会讲解这些,我们会明了NSRegularExpression 的用途


NSRegularExpression & NSTextCheckingResult NSRegularExpression 与 NSTextCheckingResult

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台