在日常应用正则表达式时,经常遇到的一类问题是,弄不清楚到底该如何转义——最明显的表现就是,搞不懂究竟要使用多少个反斜线,大部分时候,只好盲目尝试,直到测试成功为止。但是,许多时候,这样的方法并不能完整解决问题。
为了彻底解决这类问题,我们需要弄清楚正则表达式与字符串的关系:它其实很简单,根据我的经验,我们只需要牢记下面两条原则:
1.正则表达式不等于字符串,但必须以字符串的形式给出
大多数语言中都存在正则表达式(regex)对象,譬如Java语言中的Pattern对象,即使没有提供专门的对象,也需要用某些特殊的字符来标注正则表达式,譬如PHP中常用的反斜线‘/’。另一方面,正则表达式对某些字符或字符序列有自己的规定,不同于字符串的规定,譬如‘\b’在正则表达式中表示单词分界符,而在普通字符串中表示退格字符。因此我们可以说,正则表达式并不等于普通的字符串。
但是,正则表达式又终究是一种处理文本的语言,因此,我们写出的所有正则表达式,都是以字符串形式提供的。
所以,在应用正则表达式的过程中,需要进行从字符串到正则表达式本身的转换。
也就是
“源代码中的字符序列”->“字符串”->“正则表达式”
譬如,在Java语言中,正则表达式中的‘\b’,在以字符串表示时,必须写做
String regex = "\\b";
其中第1个反斜线用来转义第2个反斜线,这样,正则表达式接受到的才是真正的‘\b’,对应正则表达式中的单词分界符,如果我们写
String regex = "\b";
编译也没有问题,但此时正则表达式接受的是一个字符,即退格符。
需要注意的是,在Java和C#之类的语言中,如果字符串中出现的字符序列无法识别,编译会出错,譬如这样:
String regex = "\w";
编译无法通过,因为根据字符串的规定,‘\w’不是一个合法的转义序列,在字符串这一关就卡住了。
但是在PHP和Python之类的语言中,这样写却没有问题。原因在于,如果PHP和Python发现字符串中有无法识别的转义序列,会原封不动地保存下来,这样,正则表达式接受到的,仍然是‘\w’。
当然,我们也可以在这些语言中使用‘\\w’,结果是一样的,因为此时,在进行字符串处理时,第1个反斜线转义了第2个反斜线,正则表达式接受到的,仍然是‘\w’。
这是在实际开发中非常迷惑人的一点,只要我们弄清了正则表达式和字符串的关系,就不会再感到迷惑。
2.正则表达式中单独出现的反斜线也需要转义
在正则表达式中,反斜线通常与其它字符一起构成特殊的结构,譬如‘\d’用来匹配数字字符,‘\s’用来匹配空白字符,‘\1’用来反向引用第一个分组捕获的文本。
可是,如果我们在正则表达式中,仅仅需要表示“反斜线”字符本身,该如何做呢?
其实,正则表达式对这个问题的处理,与字符串的处理是一样的,也就是说,在正则表达式中,必须用转义序列‘\\’来表示单个反斜线。
这个规定会带来一个有趣的问题:正则表达式中单独出现的反斜线字符,在正则表达式的层面,必须以转义序列‘\\’来表示,然而,每个反斜线,在表示正则表达式的字符串中,又必须以转义序列‘\\’来表示。所以,在字符串中,必须写出四个反斜线‘\\\\’,才能对应到正则表达式中单独出现的一个反斜线字符:在字符串处理层面,它们会被识别为两个反斜线‘\\’,在正则表达式的层面,它们会被识别为单个反斜线字符‘\’。