在 InjectionIII 中,有下面的代码对字符串进行了扩展,这篇文章是对这些方法的分析。
fileprivate extension StringProtocol {
subscript(range: NSRange) -> String? {
return Range(range, in: String(self)).flatMap { String(self[$0]) }
}
func escaping(_ chars: String, with template: String = "\\$0") -> String {
return self.replacingOccurrences(of: "[\(chars)]",
with: template.replacingOccurrences(of: "\\", with: "\\\\"),
options: [.regularExpression])
}
func unescape() -> String {
return replacingOccurrences(of: #"\\(.)"#, with: "$1",
options: .regularExpression)
}
}
其中 subscript 的功能相对简单,下面来分析一下 escaping 和 unescape 方法
正则匹配中的 $0 和 \\
func escaping(_ chars: String, with template: String = "\\$0") -> String {
return self.replacingOccurrences(of: "[\(chars)]",
with: template.replacingOccurrences(of: "\\", with: "\\\\"),
options: [.regularExpression])
}
其中 template 参数的默认值为 "\0",其中一个 `\` 为反义符,所以 template 的真实值时 `\0。同理,template.replacingOccurrences(of: "\", with: "\\") 的结果为:\$0`。
所以,在 template 缺省的情况下,方法可以的执行效果为:
func escaping(_ chars: String) -> String {
return self.replacingOccurrences(of: "[\(chars)]",
with: "\\\\$0"),
options: [.regularExpression])
}
在字符串的替换方法 replacingOccurrences 中的 options 参数为 [.regularExpression],也就是通过正则表达式的方式进行匹配替换。在正则表达式中,需要提取的内容为 [\(chars)],也就是将包含在 chars 字符,替换成为 \\$0。
那 \\$0 是什么意思呢?这个字符串又可以分为两部分 \\ 和 $0。在正则表达式中,\ 又是反义符,所以 \\ 表达的意思为 \ 字符。$0 为一个模板,将会用匹配到的字符串替换掉该模板。下面通过一个示例来说明 $0 的作用:
let string = "abc"
print(string.replacingOccurrences(of: "b", with: "$B$"))
输出:
a$B$c
有了
$0,是不是有$1和$2呢?下面会讲。
也就是说 \\$0 表达的意思是:在匹配到的字符串前面加上 \。所以 escaping(_ chars:) 的方法的作用为:将包含在 chars 中的字符的前面加上 \。
字符串前后的 #
func unescape() -> String {
return replacingOccurrences(of: #"\\(.)"#, with: "$1",
options: .regularExpression)
}
在这个方法的参数中,先看 #"\\(.)"#。
扩展字符串分隔符
字符串和字符 中有一段对描述:
您可以将字符串文字放在扩展分隔符中,这样字符串中的特殊字符将会被直接包含而非转义后的效果。将字符串放在引号(")中并用数字符号(#)括起来。例如,打印字符串文字
#"Line 1 \nLine 2"#会打印换行符转义序列(\n)而不是给文字换行。如果需要字符串文字中字符的特殊效果,请匹配转义字符(\)后面添加与起始位置个数相匹配的 # 符。 例如,如果您的字符串是
#"Line 1 \nLine 2"#并且您想要换行,则可以使用#"Line 1 \#nLine 2"#来代替。 同样,###"Line1 \###nLine2"###也可以实现换行效果。扩展分隔符创建的字符串文字也可以是多行字符串文字。 您可以使用扩展分隔符在多行字符串中包含文本 """,覆盖原有的结束文字的默认行为。例如:
let threeMoreDoubleQuotationMarks = #""" Here are three more double quotes: """ """# print(threeQuotationMarks)输入出:Here are three more double quotes: """
总结来说,我们可以通过 扩展字符串分隔符 来将字符串中的特殊字符直接包含而非转义后的效果。
所以参数中的 #"\\(.)"# 就是想表达 \\(.) 的字符串。同样因为正则表达式中 \ 有反义符的作用,所以在正则表达式中表示的内容为 \(.)。
正则表达式中的分组
在正则表达式中可以使用括号 () 进行分组,想要获取到各个分组中的内容,就是使用 $ + 分组序列。所以$1 就是配到字符串中第 1 分组中的内容。
在正在表达式中 . 表示匹配除 \n 和 \r 之外的任何单个字符。
所以正则表达式想要查找的内容为:如果发现一个字符前面有 \,则只保留这个字符,也就是去掉字符前面的 \。
参考
How to replace occurences in string using groups in Swift? juejin.cn/post/684490…