为什么正则表达式如此引起争议?

在探索正则表达式(也称为RegEx-es)时,有许多人似乎将正则表达式视为圣杯。 看起来如此复杂的东西 - 只是任何问题的答案。 他们倾向于认为每个问题都可以用正则表达式来解决。

另一方面,也有很多人不惜一切代价避免正规表达。 他们试图找到正则表达式的一种解决方法,并且为了它而接受额外的编码,即使正则表达式是更紧凑的解决方案。

为什么正则表达式被认为如此有争议? 对他们的工作方式是否存在广泛的误解? 或者它可能是一个广泛的信念,正则表达式通常很慢?


我不认为人们反对正则表达式是因为它们很慢,但是因为它们很难读写,而且很难正确表达。 虽然在某些情况下,正则表达式可以为问题提供有效的紧凑解决方案,但它们有时会陷入更易于使用易于阅读和维护的代码段的情况。


使正则表达式可维护

将之前称为“正则表达式”的模式神秘化的一大进步是Perl的/x regex标志 - 有时在嵌入时写入(?x) - 允许空格(换行符,缩进)和注释。 这严重提高了可读性并因此提高了可维护性。 白色空间允许认知分块,所以你可以看到什么组。

现在,现代模式现在也支持相对编号和指定的反向引用。 这意味着你不再需要计算捕获组来确定你需要$47 $4 。 这有助于创建可以包含在其他模式中的模式。

以下是一个相对编号的捕获组的例子:

$dupword = qr{ b (?: ( w+ ) (?: s+ g{-1} )+ ) b }xi;
$quoted  = qr{ ( ["'] ) $dupword  1 }x;

这里是命名捕获的优越方法的一个例子:

$dupword = qr{ b (?: (?<word> w+ ) (?: s+ k<word> )+ ) b }xi;
$quoted  = qr{ (?<quote> ["'] ) $dupword  g{quote} }x;

语法正则表达式

最重要的是 ,这些命名捕获可以放在一个(?(DEFINE)...)块中,这样就可以将该声明从执行模式中单个命名元素的过程中分离出来。 这使得它们在模式中像子例程一样行事。
这种“语法正则表达式”的一个很好的例子可以在这个答案和这个答案中找到。 这些看起来更像是一个语法声明。

正如后者提醒你的那样:

...确保永远不写线噪音模式。 你不必,也不应该。 禁止使用禁止空白,注释,子例程或字母数字标识符的编程语言。 所以在你的模式中使用所有这些东西。

这不能过分强调。 当然,如果你不在你的模式中使用这些东西,你经常会制造一场噩梦。 但是,如果你确实使用了它们,那么你不需要。

下面是现代语法模式的另一个例子,这是一个用于解析RFC 5322的例子:use 5.10.0;

$rfc5322 = qr{

   (?(DEFINE)

     (?<address>         (?&mailbox) | (?&group))
     (?<mailbox>         (?&name_addr) | (?&addr_spec))
     (?<name_addr>       (?&display_name)? (?&angle_addr))
     (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
     (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
     (?<display_name>    (?&phrase))
     (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

     (?<addr_spec>       (?&local_part) @ (?&domain))
     (?<local_part>      (?&dot_atom) | (?&quoted_string))
     (?<domain>          (?&dot_atom) | (?&domain_literal))
     (?<domain_literal>  (?&CFWS)? [ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                   ] (?&CFWS)?)
     (?<dcontent>        (?&dtext) | (?&quoted_pair))
     (?<dtext>           (?&NO_WS_CTL) | [x21-x5ax5e-x7e])

     (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#$%&'*+-/=?^_`{|}~])
     (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
     (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
     (?<dot_atom_text>   (?&atext)+ (?: . (?&atext)+)*)

     (?<text>            [x01-x09x0bx0cx0e-x7f])
     (?<quoted_pair>      (?&text))

     (?<qtext>           (?&NO_WS_CTL) | [x21x23-x5bx5d-x7e])
     (?<qcontent>        (?&qtext) | (?&quoted_pair))
     (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                          (?&FWS)? (?&DQUOTE) (?&CFWS)?)

     (?<word>            (?&atom) | (?&quoted_string))
     (?<phrase>          (?&word)+)

     # Folding white space
     (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
     (?<ctext>           (?&NO_WS_CTL) | [x21-x27x2a-x5bx5d-x7e])
     (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
     (?<comment>         ( (?: (?&FWS)? (?&ccontent))* (?&FWS)? ) )
     (?<CFWS>            (?: (?&FWS)? (?&comment))*
                         (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

     # No whitespace control
     (?<NO_WS_CTL>       [x01-x08x0bx0cx0e-x1fx7f])

     (?<ALPHA>           [A-Za-z])
     (?<DIGIT>           [0-9])
     (?<CRLF>            x0d x0a)
     (?<DQUOTE>          ")
     (?<WSP>             [x20x09])
   )

   (?&address)

}x;

这不是很了不起 - 精彩? 您可以采用BNF风格的语法并将其直接转换为代码,而不会丢失其基本结构!

如果现代的语法模式对您来说仍然不够,那么Damian Conway的辉煌的Regexp::Grammars模块提供了更清晰的语法,并具有出色的调试功能。 以下是将RFC 5322重新转换为来自该模块的模式的相同代码:

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";

my $rfc5322 = do {
    use Regexp::Grammars;    # ...the magic is lexically scoped
    qr{

    # Keep the big stick handy, just in case...
    # <debug:on>

    # Match this...
    <address>

    # As defined by these...
    <token: address>         <mailbox> | <group>
    <token: mailbox>         <name_addr> | <addr_spec>
    <token: name_addr>       <display_name>? <angle_addr>
    <token: angle_addr>      <CFWS>? < <addr_spec> > <CFWS>?
    <token: group>           <display_name> : (?:<mailbox_list> | <CFWS>)? ; <CFWS>?
    <token: display_name>    <phrase>
    <token: mailbox_list>    <[mailbox]> ** (,)

    <token: addr_spec>       <local_part> @ <domain>
    <token: local_part>      <dot_atom> | <quoted_string>
    <token: domain>          <dot_atom> | <domain_literal>
    <token: domain_literal>  <CFWS>? [ (?: <FWS>? <[dcontent]>)* <FWS>?

    <token: dcontent>        <dtext> | <quoted_pair>
    <token: dtext>           <.NO_WS_CTL> | [x21-x5ax5e-x7e]

    <token: atext>           <.ALPHA> | <.DIGIT> | [!#$%&'*+-/=?^_`{|}~]
    <token: atom>            <.CFWS>? <.atext>+ <.CFWS>?
    <token: dot_atom>        <.CFWS>? <.dot_atom_text> <.CFWS>?
    <token: dot_atom>        <.CFWS>? <.dot_atom_text> <.CFWS>?
    <token: dot_atom_text>   <.atext>+ (?: . <.atext>+)*

    <token: text>            [x01-x09x0bx0cx0e-x7f]
    <token: quoted_pair>      <.text>

    <token: qtext>           <.NO_WS_CTL> | [x21x23-x5bx5d-x7e]
    <token: qcontent>        <.qtext> | <.quoted_pair>
    <token: quoted_string>   <.CFWS>? <.DQUOTE> (?:<.FWS>? <.qcontent>)*
                             <.FWS>? <.DQUOTE> <.CFWS>?

    <token: word>            <.atom> | <.quoted_string>
    <token: phrase>          <.word>+

    # Folding white space
    <token: FWS>             (?: <.WSP>* <.CRLF>)? <.WSP>+
    <token: ctext>           <.NO_WS_CTL> | [x21-x27x2a-x5bx5d-x7e]
    <token: ccontent>        <.ctext> | <.quoted_pair> | <.comment>
    <token: comment>         ( (?: <.FWS>? <.ccontent>)* <.FWS>? )
    <token: CFWS>            (?: <.FWS>? <.comment>)*
                             (?: (?:<.FWS>? <.comment>) | <.FWS>)

    # No whitespace control
    <token: NO_WS_CTL>       [x01-x08x0bx0cx0e-x1fx7f]

    <token: ALPHA>           [A-Za-z]
    <token: DIGIT>           [0-9]
    <token: CRLF>            x0d x0a
    <token: DQUOTE>          "
    <token: WSP>             [x20x09]

    }x;

};


while (my $input = <>) {
    if ($input =~ $rfc5322) {
        say Dumper %/;       # ...the parse tree of any successful match
                              # appears in this punctuation variable
    }
}

perlre手册页中有很多好东西,但基本正则表达式设计特性的这些重大改进决不仅限于Perl。 事实上,pcrepattern的手册页可能会更容易阅读,并涵盖相同的领域。

现代模式与您在有限自动机课程中教授的原始东西几乎没有任何共同之处。


正则表达式是一个很好的工具,但是人们会想:“嘿,多么伟大的工具,我会用它来做X!” 其中X是一种不同的工具更适合(通常是解析器)。 这是使用锤子的标准,您需要螺丝刀的问题。

链接地址: http://www.djcxy.com/p/76873.html

上一篇: Why are regular expressions so controversial?

下一篇: HOw to parse a div using Regular Expressions in java?