首页 文章

为什么正则表达式如此具有争议性? [关闭]

提问于
浏览
200

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

另一方面,也有许多人试图不惜一切代价避免使用正则表达式 . 他们试图找到解决正则表达式的方法并接受额外的编码,即使正则表达式是一个更紧凑的解决方案 .

为什么正则表达式被认为是如此有争议?是否存在关于它们如何工作的普遍误解?或者说,正则表达式通常很慢,这是一个广泛的信念吗?

22 回答

  • 0

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

  • 0

    使正则表达式可维护

    揭开以前被称为“正则表达式”的模式的一个重大进展是Perl的 /x 正则表达式标志 - 有时在嵌入时写成 (?x) - 允许空格(换行,缩进)和注释 . 这极大地提高了可读性,从而提高了可维护性白色空间允许认知分块,因此您可以看到哪些组具有什么 .

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

    以下是相对编号的捕获组的示例:

    $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;
    

    语法正则表达式

    Best of all ,这些命名的捕获可以放在 (?(DEFINE)...) 块中,这样您就可以将声明与模式中各个命名元素的执行分开 . 这使它们的行为更像是模式中的子程序 .
    this answerthis one中可以找到这种“语法正则表达式”的一个很好的例子 . 这看起来更像是语法宣言 .

    正如后者提醒您:

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

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

    这是现代语法模式的另一个例子,这个用于解析RFC 5322:使用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-\x5a\x5e-\x7e])
    
         (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
         (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
         (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
         (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)
    
         (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
         (?<quoted_pair>     \\ (?&text))
    
         (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\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-\x27\x2a-\x5b\x5d-\x7e])
         (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
         (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
         (?<CFWS>            (?: (?&FWS)? (?&comment))*
                             (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
    
         # No whitespace control
         (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])
    
         (?<ALPHA>           [A-Za-z])
         (?<DIGIT>           [0-9])
         (?<CRLF>            \x0d \x0a)
         (?<DQUOTE>          ")
         (?<WSP>             [\x20\x09])
       )
    
       (?&address)
    
    }x;
    

    这不是那么了不起 - 而且非常出色?您可以采用BNF风格的语法并将其直接转换为代码而不会失去其基本结构!

    如果现代语法模式对你来说还不够,那么Damian Conway’s brilliant Regexp::Grammars module提供了更清晰的语法,并且具有出色的调试功能 . 以下是将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-\x5a\x5e-\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-\x09\x0b\x0c\x0e-\x7f]
        <token: quoted_pair>     \\ <.text>
    
        <token: qtext>           <.NO_WS_CTL> | [\x21\x23-\x5b\x5d-\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-\x27\x2a-\x5b\x5d-\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-\x08\x0b\x0c\x0e-\x1f\x7f]
    
        <token: ALPHA>           [A-Za-z]
        <token: DIGIT>           [0-9]
        <token: CRLF>            \x0d \x0a
        <token: DQUOTE>          "
        <token: WSP>             [\x20\x09]
    
        }x;
    
    };
    
    
    while (my $input = <>) {
        if ($input =~ $rfc5322) {
            say Dumper \%/;       # ...the parse tree of any successful match
                                  # appears in this punctuation variable
        }
    }
    

    the perlre manpage中有很多好东西,但基本正则表达式设计功能的这些显着改进绝不仅限于Perl . 事实上the pcrepattern manpage可能更容易阅读,涵盖相同的领域 .

    现代模式与您在有限自动机类中教授的原始事物几乎没有任何共同之处 .

  • 4

    正则表达式是一个很好的工具,但人们认为“嘿,这是一个很棒的工具,我会用它来做X!”其中X是不同工具更适合的东西(通常是解析器) . 这是使用锤子的标准,你需要一个螺丝刀问题 .

  • 119

    几乎所有我认识的人经常使用正则表达式(双关语)来自Unix-ish背景,他们使用将RE作为一流编程结构的工具,如grep,sed,awk和Perl . 由于使用正则表达式几乎没有语法上的开销,因此当他们这样做时,他们的工作效率会提高 .

    相比之下,使用RE是外部库的语言的程序员往往不会考虑正则表达式可以带到表中的内容 . 程序员的“时间成本”是如此之高,以至于a)RE从未作为他们培训的一部分出现,或者b)他们没有在RE方面“思考”并且更倾向于依赖更熟悉的模式 .

  • 0

    正则表达式允许您以紧凑的方式编写自定义有限状态机(FSM),以处理输入字符串 . 使用正则表达式很难有至少两个原因:

    • 老派软件开发涉及很多计划,纸模型和仔细思考 . 正则表达式非常适合这个模型,因为正确编写一个有效的表达式需要大量的盯着它,可视化FSM的路径 .

    现代软件开发人员更愿意敲定代码,并使用调试器逐步执行,以查看代码是否正确 . 正则表达式不能很好地支持这种工作方式 . 正则表达式的一个“运行”实际上是原子操作 . 在调试器中观察逐步执行很困难 .

    • 它真的要匹配有效输入,它是 fail to match invalid input . 为正则表达式执行"negative tests"的技术不是很先进,或者至少没有广泛使用 .

    这使得正则表达式难以阅读 . 仅仅通过查看正则表达式,需要大量注意力来想象应该被拒绝的所有可能的输入,但是被错误地接受 . 曾经尝试调试其他人的正则表达式代码?

    如果今天软件开发人员对使用正则表达式有抵触,我认为这主要是由于这两个因素 .

  • 2

    人们倾向于认为正则表达很难;但那是因为他们错了 . 编写复杂的单行,没有任何注释,缩进或命名捕获 . (你不要在一行中填写复杂的SQL表达式,没有注释,缩进或别名,不是吗?) . 是的,对很多人来说,他们没有意义 .

    但是,如果你的工作与解析文本(大概是任何网络应用程序......)有关,那么你就不会浪费自己和雇主的时间 . 有excellent resources在那里教你关于他们的一切,你需要知道,等等 .

  • 16

    因为它们缺乏普遍接受的IDE中最流行的学习工具:没有正则表达式向导 . 甚至没有Autocompletion . 你必须自己编写所有的东西 .

  • 124

    我不认为他们是那么有争议的 .

    我也认为你已经回答了自己的问题,因为你指出在任何地方使用它们是多么愚蠢(Not everything is a regular language 2)或者完全避免使用它们 . 作为程序员,您必须明智地决定正则表达式何时会帮助代码或者对代码造成伤害 . 面对这样的决定时,要记住的两个重要事项是可维护性(这意味着可读性)和可扩展性 .

    对于那些特别厌恶他们的人,我的猜测是他们从未学会正确使用它们 . 我认为大多数只花几个小时使用体面教程的人会把它们弄清楚并且很快就能流利 . 这是我建议从哪里入手:

    http://docs.python.org/howto/regex

    虽然该页面讨论了Python上下文中的正则表达式,但我发现这些信息在其他地方非常适用 . 有些东西是特定于Python的,但我相信它们清晰可闻,易于记忆 .

  • 68

    Regular Expressions: Now You Have Two Problems " is a great article from Jeff Atwood on the matter. Basically, regular expressions are "很难”!他们可以创造新的问题 . 然而,它们是有效的 .

  • 5

    正则表达式是将算术运算符与数字串联起来,我不认为它们是有争议的 . 我认为即使是像我这样相当苛刻的活动家(他们倾向于选择其他物品而不是弦乐器)也很难拒绝它们 .

  • 2

    问题是正则表达式可能非常强大,你可以用它们做一些你应该使用不同的东西 .

    一个好的程序员应该知道在哪里使用它们,哪里不知道 . 典型的例子是解析非常规语言(参见Deciding whether a language is regular) .

    如果你首先将自己限制在真正的正则表达式(没有扩展名),我认为你不会出错 . 一些扩展可以让你的生活更轻松,但如果你发现一些难以表达的真正的正则表达式,这很可能表明正则表达式不是正确的工具 .

  • 36

    你几乎可以问为什么goto是有争议的 .

    基本上,当你获得如此多的“明显”力量时,人们很容易滥用它们,因为它们不是最好的选择 . 例如,要求在正则表达式中解析CSV或XML或HTML的人数让我震惊 . 这是工作的错误工具 . 但是有些用户仍坚持使用正则表达式 .

    就个人而言,我试图找到快乐的媒介 - 使用正则表达式来获得它们的好处,并在它们不是最佳时避免使用它们 .

    请注意,正则表达式仍可用于解析CSV,XML,HTML等 . 但通常不在单个正则表达式中 .

  • 16

    我不认为“有争议的”是正确的词 .

    但是我已经看到很多例子,人们会说“我需要做什么正则表达式,这样的字符串操作?”哪些是X-Y问题 .

    换句话说,他们开始假设正则表达式是他们所需要的,但他们最好使用split(),像perl的tr ///这样的翻译,其中字符替换为其他,或只是一个索引() .

  • 7

    这是一个有趣的主题 .
    许多正则表达爱好者似乎混淆了公式的简洁性和效率 .
    最重要的是,需要大量思考的正则表达式为其作者带来了巨大的满足感,使其立即合法化 .

    但是......当性能不是问题而且你需要快速处理文本输出时,例如在Perl中,regexp是方便的 . 此外,虽然性能是一个问题,但人们可能不愿意尝试使用可能有缺陷或效率较低的自制算法来击败正则表达式库 .

    此外,例如,有很多理由可以对正则表达式进行不公平的批评

    • 正则表达式效率不高,因为构建顶级的并不明显

    • 一些程序员"forget"只能编译一次regexp多次使用(比如Java中的静态模式)

    • 一些程序员选择试用和错误策略 - 使用regexp工作更少!

  • 42

    对于包括我自己在内的很多人而言,正则表达式是一个严重的谜 . 虽然有人最终在http://regexlib.com/处创建了各种正则表达式函数的合并位置,但它很有用但很高兴报告's like looking at a math equation. I' . 现在,如果微软只创建一个正则表达式类,它将自动执行许多常见的事情,如消除字母或过滤日期 .

  • 28

    我认为学习正则表达式和维护正则表达式是不受欢迎的,大多数开发人员都是懒惰的,或者他们中的大多数都依靠外部库为他们做解析...他们依靠谷歌的答案,甚至在论坛中提问他们问题的完整代码 . 但是当实现或修改/维护正则表达式时,它们就会失败 .

    有一句流行的说法“朋友不要让朋友使用正则表达式来解析HTML”

    但就我而言,我已经使用Regex制作了完整的HTML解析器,并且我发现我的自我正则表达式更好地解析html字符串,包括速度和内存方式(如果你有一个想法,你要实现什么:))

  • 0

    我发现正则表达式有时非常宝贵 . 当我需要进行一些“模糊”搜索时,可能会替换 . 当数据可能变化并具有一定的随机性时 . 但是,当我需要进行简单的搜索和替换,或检查字符串时,我不使用正则表达式 . 虽然我认识很多人,但他们会把它用于一切 . 这是争议 .

    如果你想在墙上贴一个钉子,不要使用锤子 . 是的,它会起作用,但是当你拿到锤子时,我可以在墙上放上20个钉子 .

    正则表达式应该用于它们的设计目的,而不是更少 .

  • 52

    虽然我认为正则表达式是一个必不可少的工具,但最烦人的是有不同的实现 . 语法,修饰符和特别是“贪婪”之间的微小差异会使事情变得非常混乱,需要反复试验,有时会产生令人费解的错误 .

  • 1

    在某些情况下,我认为你必须使用它们 . 例如, Build 一个词法分析器 .

    在我看来,这是一个可以写regexp的人和没有(或几乎没有)的人的观点 . 我个人认为这是一个很好的想法,例如有效的表单输入,无论是在javascript中警告用户,还是在服务器端语言 .

  • -3

    我认为这是程序员中鲜为人知的技术 . 因此,没有广泛接受它 . 如果您有一位非技术经理来审核您的代码或审核您的工作,那么正则表达式非常糟糕 . 您将花费数小时编写一个完美的正则表达式,并且您将获得很少的标记,因为他/她已经编写了如此少的代码行 . 另外,如其他地方所述,阅读正则表达式是非常困难的任务 .

  • 4

    体积正则表达式系统(例如在lex和yacc中用于编译器定义)很好,非常有用且干净 . 在这些系统中,表达类型是根据其他类型定义的 . 这是在perl和sed代码(等等)中常见的令人难以理解的难以理解的线性噪声巨型单行正则表达式,这些正则表达式是“有争议的”(垃圾) .

  • 11

    正则表达式的最佳有效和正常用法是用于电子邮件地址格式验证 .

    这是一个很好的应用 .

    我已经无数次使用正则表达式作为TextPad中的一次性按摩平面文件,创建csv文件,创建SQL插入语句等等 .

    写得好的正则表达式不应该太慢 . 通常替代方案,如大量调用Replace,选项要慢得多 . 不妨一次性做到这一点 .

    许多情况需要完全正则的表达式而不是其他任何东

    用无害字符替换特殊的非打印字符是另一个很好的用法 .

    我当然可以想象有些代码库过度使用正则表达式而不利于可维护性 . 我从未见过自己 . 事实上我已被代码审查员所避免足够使用正则表达式 .

相关问题