8.1 位置字符
目前为止我们已经学习了字面量字符,字符集,句号这些元字符。它们可以匹配到单个字符。
位置字符的规则和之前学习的元字符有很大的区别。位置字符不匹配任何字符,它匹配的是位置,可能位于字符串的开始、中间、结尾。它可以匹配的结果锚定在字符串的特殊位置。脱字符^
可以匹配字符串的开始位置。例如^a
可以匹配abc中的a,而^b
不能匹配abc中的任何字符,因为b不在字符串的起始位置。下面我们会解释位置字符在引擎的工作原理。
$
可以匹配字符串中的结束位置,例如c$
可以匹配abc中的c,而a$
不能匹配任何字符。
一个只包含位置字符的表达式只能进行零宽匹配。有时候这非常有用,但也可能造成混乱,我们会在教程的后半部分详细介绍。
8.2 应用举例
位置字符在验证用户输入时非常有用。例如你用\d+
去验证用户的输入是否为整数,它会把qsdf4ghjk作为合法字符,因为\d+
可以匹配4。正确的表达式为^\d+$
,因为正则匹配结果的首个字符必须位于字符串的开始位置,最后一个匹配的字符必须位于字符串的结束位置,所以整个字符串只能包含一个整数。
用户在输入的时候很可能在字符串的开始或者结尾输入了不必要的空格字符,我们在做验证之前应该修剪字符串前后的空格。我们可以使用正则来查找这些空格,^\s+
可以匹配字符串前面的空格,\s+$
可以匹配后面的空格。在javascript中你可以使用/^\s+|\s+$/g
同时匹配前面和后面的空格
8.3 关于换行符
不同的引擎对于换行符的处理是不一致的。在多行匹配模式中位置字符的行为在不同引擎中也有所不同。
在Delphi,Java,和JGsoft flavor中CRLF是作为一个整体匹配的,^
匹配CRLF之后的位置,$
匹配CRLF之前的位置,但是他们不会匹配的CRLF中间的位置。在JavaScript和XPath中CRLF作为两个单独的字符,^
可以匹配到CRLF之后和中间的位置,&
可以匹配到CRLF之前和中间的位置。
8.4 忽略换行符
有一些引擎可以忽略字符串中的换行符,匹配字符串的开始位置(使用\A
)和字符串的结尾位置(使用\Z
)。即使已经开启了多行模式,它们还是会忽略字符串中间的换行符。
JavaScript,POSIX,XML,XPath不支持这个功能。
8.5 内部原理
让我们来看一下^4$
(在多行模式下)是如何匹配‘749\n486\n4’的,其中\n
表示一个换行符。正则表达式引擎首先从第一个字符7开始匹配,而第一个token是^
。由于这是一个零宽的位置字符,所以它不会和7进行匹配,而是和7的前一个字符匹配。因为7的前面没有字符,所以^
匹配成功了。下一步引擎将匹配4
这个字面量字符,由于前一个token是零宽的,所以引擎没有移动到字符串中的下一个字符,还是用4
和7匹配,这一次匹配没有成功。
匹配失败后,引擎将回到第一个token^
,并且从第二个字符4开始匹配。^
和4不能匹配成功,因为4的前一个字符是7而不是换行符。同样地,第三个字符9和第四个字符\n
都匹配失败了。
现在正则引擎开始匹配字符串的第二4,这一次匹配成功了,因为4前面的换行符可以和^
匹配。接下来引擎在正则和字符串中各自向前一步,使用token4
匹配字符4,匹配也成功了。在下一步引擎尝试将$
和字符8匹配,由于当前位置是一个字符8,而不是换行符所以匹配失败了。
于是引擎再一次回到表达式的第一个token重新开始匹配,这一次它从字符8开始匹配。和之前一样8和之后的6以及\n
都不能和^
匹配。直到最后一个字符4,4之前是一个换行符,所以这个位置可以和^
匹配成功。接下来4和4匹配成功。下一步引擎在正则表达式和字符串中各自向前一步,也就是正则中的$
和字符串中的void(在字符串的末尾之后)。事实上任何匹配单个字符的token都不能和void匹配,即使是字符集取反也不能,除了$
这个怪物。$
将匹配当前字符的前一个位置,当前字符可以是void。事实上引擎的做法是检查当前字符是否是换行符或者字符串后面的void,因为$
将匹配当前字符之前的位置。在这个例子中,当前字符是void所以$
匹配成功了。
因为$
已经是表达式中的最后一个字符,所以整个表达式匹配成功了,它匹配字符串中最后一个4。
如果文章出现错误,请给我提Issues - -
Github地址
需要进一步翻译的内容:
- 零宽匹配