教程 > 正则表达式 > 锚点 阅读:112

锚点

字符串的开始和结束符


到目前为止,我们已经了解了文字字符字符类点号。将其中之一放在正则表达式中会告诉正则表达式引擎尝试匹配单个字符。   

锚点和上面三种不同。它根本不匹配任何字符,而是匹配字符之前,之后或之间的位置。它们可用于在特定位置“固定”正则表达式匹配。符号^匹配字符串中第一个字符之前的位置。应用^a到abc匹配a。^b不匹配abc ,因为b不能在字符串开头之后立即匹配,并由^匹配。请参阅下面的正则表达式引擎的内部视图。              

同样,$匹配字符串中的最后一个字符。c$匹配abc中的c,而a$则匹配不到任何字符。         

仅由锚组成的正则表达式只能找到零长度的匹配项。这可能很有用,但也会造成本教程末尾将要解释的复杂情况。   

有用的应用程序


当使用编程语言中的正则表达式来验证用户输入时,使用锚点非常重要。如果在Perl脚本中使用代码if($input = 〜m /\d+/)来查看用户是否输入了整数,即使用户输入 qsdf4ghjk ,它也会接受输入,因为\d+与4匹配。正确使用的正则表达式是^\d+$。因为“开始的字符串”必须在匹配\d+之前匹配,和“字符串的结束”之后就必须匹配,整个字符串必须包含的数字为^\d+$能够匹配。              

用户很容易意外输入空格。当Perl从文本文件的一行中读取时,换行符也存储在该变量中。因此,在验证输入之前,最好的作法是匹配之前和之后的空白。^\s+匹配前导空格,\s+$匹配尾随空格。在Perl中,我们可以使用$input =〜 s/^\s+|\s+$//g 。方便地使用路径选择和/g允许我们在一行代码中执行此操作。      

使用^和$作为行起始和行末锚点


如果我们的字符串由多行组成,例如first line \n second line(其中\n表示换行符),则通常需要使用行而不是整个字符串。因此,本教程中讨论的大多数正则表达式引擎都可以选择扩展两个锚点的含义。然后,^可以在字符串的开头(在上述字符串中的f之前),以及在每个换行符之后(在\n和s之间)匹配。同样,$仍然在字符串的末尾(在最后一个e之后)以及在每个换行符之前(在e和\n之间)匹配。               

在Ruby和std::regex中,^和$也始终在每行的开头和结尾匹配。在Boost中,默认情况下它们在每行的开头和结尾匹配。使用ECMAScript语法时,Boost允许我们使用regex_constants::no_mod_m将其关闭。           

这里讨论的所有其他编程语言和库中,我们必须显式激活此扩展功能。传统上称为“多行模式”。在Perl中,我们可以通过在正则表达式代码之后添加一个模式修饰符m来完成此操作,如下所示:m/^regex$/m;。在.NET中,当我们指定RegexOptions.Multiline时,锚点在换行符之前和之后匹配,例如在Regex.Match(“string”,“regex”,RegexOptions.Multiline)中。    

换行符


关于点号.的教程在前面已经讨论过,以及各种正则表达式将哪些字符视为换行符。当处于多行模式下以及$在最终结束之前匹配时,这对锚点的影响也一样大。锚点处理由单个字符组成的换行符,其方式与每种正则表达式中的点号.相同。  

对于锚点,还需要考虑CR和LF成对出现,并且正则表达式将这两个字符都视为换行符。Delphi,Java和JGsoft将CRLF视为不可分割的对。^在CRLF之后匹配,$在CRLF之前匹配,但是在CRLF对中间都不匹配。JavaScript和XPath将CRLF对视为两个换行符。^在CRLF的中间和之后匹配,而$在CRLF的中间和中间匹配。                

字符串的最初起点和终点


\A仅在字符串开头匹配。同样,\Z仅在字符串末尾匹配。这两个token在换行符之间永远不匹配。即使在打开“多行模式”时,本教程中讨论的所有正则表达式也是如此。     

JavaScript的,POSIX,XML和XPath的不支持\A和\Z 。因此我们必须为此使用^和$。       

POSIX正则表达式的GNU扩展使用\‘(反引号)匹配字符串的开头,并使用\' (单引号)匹配字符串的结尾。      

以换行符结尾的字符串


因为从文件中读取一行时,Perl返回一个以换行符结尾的字符串,所以即使关闭了多行模式,Perl的正则表达式引擎也会在字符串结尾处的换行符之前的位置匹配$ 。Perl还会在字符串的末尾匹配$ ,而不管该字符是否是换行符。因此,无论目标字符串是123还是123\n,^\d+$都可以匹配123。           

现在大多数正则表达式都复制了这种行为。其中包括.NET,Java,PCRE,Delphi,PHP和Python。此行为与任何模式修饰符(例如“多行模式”)无关。      

在除Python以外的所有这些版本中,\Z也会在最后一行换行之前匹配。如果只想在字符串的绝对末尾进行匹配,请使用\z (小写z而不是大写Z)。\A\d+\z与123\n不匹配。\z在换行符之后匹配,而速记字符类不匹配。            在Python中,\Z仅在字符串的末尾匹配。Python不支持\z 。   

以多个换行符结尾的字符串


如果字符串以多个换行符结尾并且关闭了多行模式,则$仅在最后一个换行符之前匹配。这和\Z是相同的,无论多行模式如何,\Z都是如此。    

Boost是唯一的例外。在Boost中,\Z可以在任意数量的尾随换行符之前以及字符串的末尾进行匹配。因此,如果目标字符串以三个换行符结尾,则Boost的\Z可以匹配四个位置。与其他所有样式一样,Boost的\Z独立于多行模式。关闭多行模式(Boost默认情况下处于启用状态)时,Boost的$仅在字符串的末尾匹配。        

正则表达式引擎内部


让我们看看在多行模式下尝试将^4$匹配749\n486\n4 (其中\ n代表换行符)时会发生什么。与往常一样,正则表达式引擎从第一个字符7开始。正则表达式中的第一个标记是^ 。由于此令牌是零长度令牌,因此引擎不会尝试将其与字符进行匹配,而是将其与正则表达式引擎到目前为止到达的字符之前的位置进行匹配。^确实与7之前的位置匹配。然后,引擎前进到下一个正则表达式token:4 。由于前面的标识是零长度,正则表达式引擎并没有推进到字符串中的一个字符。它保持在7 。4是文字字符,与7不匹配。正则表达式没有其他排列,因此引擎从下一个字符4的第一个正则表达式令牌重新启动。这次,^不能与4之前的位置匹配。此位置前面有一个字符,并且该字符不是换行符。引擎在9继续运行,然后再次出现故障。\n的下一次尝试也失败。再次,\n之前的位置前面是字符9 ,并且该字符不是换行符。                        

然后,正则表达式引擎到达字符串中的第二个4 。所述^可以匹配在之前的位置4 ,因为它是由一个新行字符之前。再次,正则表达式引擎前进到下一个正则表达式标记4 ,但不前进字符串中的字符位置。4匹配4 ,并且引擎同时前移正则表达式token和字符串字符。现在,引擎尝试在8之前的位置匹配$ 。$在这里不能匹配,因为此位置后跟一个字符,并且该字符不是换行符。            

再一次,引擎必须尝试再次匹配第一个token。以前,它已在第二个4处成功匹配,因此引擎将在下一个字符8处插入符号不匹配的位置。同样在6和换行符。    

最后,正则表达式引擎尝试匹配第一个token ^。成功之后,引擎成功将4与4匹配。当前的正则表达式token前进到$ ,当前字符前进到字符串中的最后一个位置:字符串后面的空白。任何需要字符匹配的正则表达式token都不能在此处匹配。甚至没有否定字符类。但是,我们试图匹配一个$,而强大的$却是一个奇怪的野兽。它是零长度,因此它尝试匹配当前字符之前的位置。此“字符”在字符串后的空格并不重要。实际上,$检查当前字符。它必须是换行符或字符串后的空白符,以使$与当前字符之前的位置匹配。由于在示例之后就是这种情况,因此$可以成功匹配。         

由于$是正则表达式中的最后一个令牌,因此引擎已找到成功的匹配项:字符串中的最后一个4。      

查看笔记

扫码一下
查看教程更方便