2.6 匹配完整单词
问题描述
创建一个正则表达式来匹配My cat is brown中的cat,同时不能匹配category或是bobcat。再创建一个正则表达式来匹配staccato中的cat,同时不能匹配到上面的3个目标字串。
解决方案
单词边界
\bcat\b
正则选项:无
正则流派:.NET、Java、JavaScript、PCRE、Perl、Python、Ruby
非单词边界
\Bcat\B
正则选项:无
正则流派:.NET、Java、JavaScript、PCRE、Perl、Python、Ruby
讨论
单词边界
正则表达式记号‹b›被称作是单词边界(word boundary)。它会匹配一个单词的开始或结束。就它自身而言,所产生的是一个长度为0的匹配。‹b›也是一个定位符,这与在前一个小节中介绍过的记号是一样的。
严格来讲,‹b›会匹配如下3种位置:
在目标文本的第一个字符之前,如果第一个字符是单词字符;
在目标文本的最后一个字符之后,如果最后一个字符是单词字符;
在目标文本的两个字符之间,其中一个是单词字符,而另外一个不是单词字符。
如果要使用正则表达式来进行“只匹配完整单词”的查找,那么只需要把该单词放在两个单词边界之间,就像我们前面给出的‹bcatb›。第一个‹b›要求‹c›出现在字符串的最开始处,或者是在一个非单词字符后面。第二个‹b›要求‹t›出现在字符串的最末尾处,或者是在一个非单词字符前面。换行符被认为是非单词字符。如果换行符紧跟在一个单词字符后面,那么‹b›会匹配换行符后面的位置。它同样会匹配紧跟着一个单词字符之前的换行符。这样的话,一个占据了一整行的单词也可以通过一个“只匹配完整单词”的查找来发现。‹b›不会受到“多行”模式或是‹(?m)›的影响,这也是本书把“多行”模式称为“^和$匹配换行处”的原因之一。
本书中介绍的所有流派都不提供可以只匹配单词之前或者只匹配单词之后的单独记号。除非想要创建一个正则式只包含单词边界,而不包含任何其他内容,否则这些单独记号是不必要的。在正则表达式中,位于‹b›之前和之后的记号会决定‹b›可以匹配的位置。在‹bx›和‹!b›中的‹b›只会匹配一个单词的开始。而在‹xb›和‹b!›中的‹b›只会匹配一个单词的结束。‹xbx›和‹!b!›则永远不会匹配任何位置。
如果确实只想匹配一个单词之前或之后的位置,可以使用顺序环视(lookahead)和逆序环视(lookbehind)。实例2.16讲解了顺序环视和逆序环视。这种方法无法在JavaScript和Ruby 1.8中使用,因为它们不支持逆序环视。正则式‹(?
非单词边界
‹B›会匹配在目标文本中‹b›不匹配的每一个位置。换句话说,‹B›会匹配不属于单词开始或结束的每一个位置。
严格来讲,‹B›匹配如下5个位置:
在目标文本的第一个字符之前,如果第一个字符不是单词字符;
在目标文本的最后一个字符之后,如果最后一个字符不是单词字符;
在两个单词字符之间;
在两个非单词字符之间;
空串。
‹BcatB›会匹配staccato中的cat。但是不会匹配My cat is brown、category或者bobcat中的cat。
如果想要进行与“只匹配完整单词”相反的查找(也就是说,不包括My cat is brown,但是包括staccato、category和bobcat),就需要采用多选结构来把‹Bcat›和‹catB›组合成为‹Bcat|catB›。其中,‹Bcat›会匹配staccato和bobcat中的cat。‹catB›会匹配category中的cat(如果‹Bcat›没有匹配到,那么它也可以匹配staccato)。实例2.8会详细讲解多选结构。
单词字符
我们前面一直在讲单词边界,但是却没有涉及什么是单词字符(word character)。单词字符就是可以在单词中出现的字符。在实例2.3中的“简写”小节中,我们讨论了哪些字符是包含在‹w›中的,即单个的单词字符。但是,对于‹b›来说情形则有些不同。
虽然本书中的所有流派都支持‹b›和‹B›,但是它们对于到底哪些字符属于单词字符则有所不同。
在.NET、JavaScript、PCRE、Perl、Python和Ruby中,‹b›都会匹配两个字符之间的位置,其中一个字符可以由‹w›匹配,而另外一个字符则可以由‹W›匹配。‹B›则总是匹配同时被‹w›或‹W›匹配的两个字符之间的位置。
JavaScript、PCRE和Ruby只把ASCII字符看做是单词字符。‹w›因此与‹[a-zA-Z0-9_]›是完全等同的。在这些流派中,你可以对像英语这样的,单词完全由不含读音符号的A到Z的字母组成的情况,进行“只匹配完整单词”的查找。但是这些流派对于其他语言,如西班牙语或俄语,就无法进行“只匹配完整单词”的查找。
.NET把所有语言字母表中的字母和数字都当作单词字符。你可以对任意语言中的单词进行“只匹配完整单词”的查找,这其中也包括不使用拉丁字母的语言。
Python则为你提供了一个选项。在Python 2.x中,只有在创建正则式时传递了UNICODE或是U标志,非ASCII的字符才会被包括进来。在Python 3.x中,则默认包含非ASCII字符,不过可以使用ASCII或A标志排除它们。这些标志会对‹b›和‹w›产生相同的影响。
在Perl中,则Perl的版本和/adlu标志决定‹w›是纯ASCII字符还是包括全部Unicode字母、数字和下划线。实例2.3“简写”一节讲解了更多细节。在所有的Perl版本中,‹b›与‹w›保持一致。
Java则表现得不是很一致。在Java 4到Java 6中,‹w›只匹配ASCII字符。在Java 7中,‹w›默认值匹配ASCII字符,如果设置了UNICODE_CHARACTER_CLASS标志则也匹配Unicode字符。但是Java所有版本‹b›都是支持Unicode的,包括任何字母表。在Java 4到Java 6中,‹bwb›会匹配一个单个的英语字母、数字或是在任何语言中都不会作为单词一部分出现的下划线。‹bкошкаb›会正确匹配cat在俄语中对应的单词,因为‹b›是支持Unicode的。但是‹w+›不会匹配任何泰国语单词,因为在Java 4到Java 6中‹w›只会匹配ASCII字符。