“现在您有两个问题”是什么意思?


200

一个流行的报价杰米Zawinski撰写

有些人在遇到问题时会认为“我知道,我会使用正则表达式”。现在他们有两个问题。

该报价应如何理解?


46
第二个问题是他们正在使用正则表达式,但仍未解决第一个问题,因此出现了两个问题。
Ampt

24
@Euphoric-实际上,好的代码短-但没有含糊的简洁。
2014年

24
@IQAndreas:我认为这是半幽默的。现在的评论是,如果您不小心,使用正则表达式会使情况变得更糟,而不是更好。
FrustratedWithFormsDesigner 2014年

145
有些人在尝试解释某些内容时会认为“我知道,我将使用Jamie Zawinski的语录”。现在他们有两件事要解释。
2014年

Answers:


220

某些编程技术通常不为程序员所理解(正则表达式浮点数PerlAWKIoC ... )。

这些是解决正确问题集的强大工具。正则表达式对于匹配正则语言特别有用。问题的症结在于:很少有人知道如何描述常规语言(它是计算机科学理论/语言学的一部分,它使用有趣的符号-您可以在Chomsky层次结构中进行了解)。

处理这些问题时,如果错误使用它们,就不可能真正解决您原来的问题。使用正则表达式来匹配HTML(这种情况太常见了)将意味着您错过边缘情况。现在,您仍然遇到了尚未解决的原始问题,并且通过使用错误的解决方案引入了另一个细微的错误。

这并不是说不应该使用正则表达式,而是应该努力理解正则表达式可以解决的问题以及不能明智地解决和使用的问题。

维护软件的关键是编写可维护的代码。使用正则表达式可能会违反该目标。在使用正则表达式时,您已经以特定的领域特定语言编写了小型计算机(特别是非确定性有限状态自动机)。用这种语言编写“ Hello world”等价物并获得基本的信任很容易,但是需要对常规语言的理解来限制进一步发展,以避免编写可能很难识别和修复的其他错误(因为它们不是正则表达式所在程序的一部分)。

因此,现在您遇到了一个新问题;您选择了正则表达式的工具来解决它(当它不合适时),现在您有两个bug,因为它们隐藏在另一层抽象中,所以很难找到它们。


8
我不确定perl本身是否属于程序员不
容易

21
@crad关于perl的更多信息……很多人都听说过它在那里流行。我仍然喜欢兰德谈话中的浮点数:“现在您有2.00000152问题”

56
@crad有些人在遇到问题时会认为“我知道,我会使用perl”。现在他们有$(^ @#%()^%)(#)问题。
迈克尔·汉普顿

4
@Jens,如果有的话,PCRE与传统regex相比具有的强大功能使它成为更诱人的解决方案,并且更难以维护。在扩展有限自动机以有效匹配Perl兼容的正则表达式中探讨了PCRE匹配的有限自动机,这是一件很简单的事情。至少与传统的正则表达式,可以得到他们围绕它头上没有一次必要的概念理解的麻烦。

6
你说的对。正则表达式实际上是第二种平凡的语言。即使原始程序员能胜任主要语言和所使用的正则表达式的风格,添加“第二语言”也意味着维护人员会同时了解两者的几率较低。更不用说正则表达式的可读性通常低于“宿主”语言。
JS。

95

正则表达式-特别是非琐碎的正则表达式-可能难以编码,理解和维护。您只需要查看标记[regex]为Stack Overflow的问题数,发问者认为问题的答案是正则表达式,并且随后被卡住了。在很多情况下,可以(也许应该)以不同的方式解决问题。

这意味着,如果您决定使用正则表达式,则现在有两个问题:

  1. 您想解决的原始问题。
  2. 正则表达式的支持。

基本上,我认为他的意思是,如果没有其他解决问题的方法,则只应使用正则表达式。另一个解决方案可能会更易于编码,维护和支持。它可能会变慢或降低效率,但是如果不是很关键,那么维护和支持的简便性应该是首要考虑的问题。


27
更糟糕的是:它们的功能足以欺骗人们尝试使用它们来解析他们无法解析的内容,例如HTML。在SO上看到有关“如何解析HTML?”的众多问题。
Frank Shearar

6
在某些情况下,正则表达式很棒。在许多其他情况下,情况并非如此。另一方面,这是一个令人绝望的恐怖坑。当某人第一次了解它们并开始在各处看到应用程序时,通常会出现此问题。另一句著名的名言:“当您拥有的唯一工具是锤子时,一切看起来都像钉子。”
托德·威廉姆森

3
这是否意味着通过SO [c#]标记中的问题数量,它是最难理解的编程语言?

2
我宁愿看到一个复杂的正则表达式,而不是一连串的字符串方法调用。OTOH,我真的很讨厌看到正则表达式被滥用来解析复杂的语言。
凯文·克莱恩

5
“基本上,我认为他的意思是,如果没有其他方法可以解决您的问题,则仅应使用正则表达式。任何其他解决方案都将更易于编码,维护和支持。” -严重不同意。正则表达式是出色的工具,您只需要知道其局限性即可。可以使用正则表达式更优雅地编码许多任务。(但是,仅举一个例子,您不应使用它们来解析HTML)
Karoly Horvath 2011年

69

尽管有一定道理,但这主要是开玩笑的笑话。

对于某些任务,正则表达式非常适合。我曾经用一个正则表达式替换了500行手动编写的递归下降解析器代码,而这大约需要10分钟才能完全调试。人们说正则表达式很难理解和调试,但是适当应用的正则表达式几乎不像手工设计的大型解析器那么难调试。在我的示例中,调试非正则表达式解决方案的所有边缘情况花了两个星期。

但是,对本叔叔的解释是:

表现力强,责任重大。

换句话说,正则表达式为您的语言增加了表达能力,但这使程序员更有责任为给定任务选择最易读的表达方式。

对于正则表达式,有些事情一开始看起来很不错,但事实并非如此。例如,任何带有嵌套标记的东西,例如HTML。当更简单的方法更清晰时,有时人们使用正则表达式。例如,string.endsWith("ing")比等效的正则表达式更容易理解。有时,人们试图将一个大问题塞入一个正则表达式中,在这种情况下,将其分解为更合适的方法。有时人们无法创建适当的抽象,一遍又一遍地重复一个正则表达式,而不是创建一个功能强大的函数来完成相同的工作(也许是在正则表达式内部实现的)。

由于某种原因,正则表达式有一种怪异的趋势,可为常规软件工程原理(如单一职责和DRY)造成盲点。这就是为什么即使是爱他们的人有时也会发现他们有问题的原因。


10
本叔叔还不是每次都说“完美的结果”吗?也许这就是为什么人们如此对正则表达式感到满意的原因……
Andrzej Doyle

4
正则表达式的有关HTML的问题使经验不足的开发人员感到震惊,因为HTML具有上下文无关的语法,而不是常规语法:正则表达式可用于某些简单的HTML(或XML)解析(例如,从命名锚标记中获取URL),但是不太适合任何复杂的事情。为此,DOM解析更为合适。相关阅读:Chomsky层次结构

53

杰夫·阿特伍德(Jeff Atwood)在讨论此报价的博客文章中提出了另一种解释:正则表达式:现在您有两个问题 (感谢链接的欣快感

通过分析原始1997年主题中Jamie的帖子全文,我们发现以下内容:

Perl的本性鼓励使用正则表达式,几乎可以排除所有其他技术。它们是从点A到点B的最“明显”(至少对于不了解的人来说)最遥远的方式。

第一个引号太浮夸了,不能被认真对待。但是,我完全同意。杰米(Jamie)试图说明的重点是:正则表达式本身并不是邪恶的,而是正则表达式的过度使用是邪恶的。

即使你完全了解的正则表达式,你碰上金锤子问题,试图解决使用正则表达式,出了问题时,它会更容易和更清晰的做常规代码同样的事情(见CodingHorror:正则表达式使用与Regex滥用)。

还有另一篇关于报价内容的博客文章,比Atwood更为详细:Jeffrey Friedl的博客:著名的“现在有两个问题”报价的来源


3
在我看来,这是最好的答案,因为它增加了上下文。jwz对正则表达式的批评与Perl一样多。
Evicatos 2014年

3
@Evicatos在另一个博客文章中,对1997年的同一线程进行了更多研究:regex.info/blog/2006-09-15/247
IQAndreas 2014年

30

这个报价有几件事情发生。

  1. 报价是较早笑话的重述:

    每当遇到问题时,有人会说“让我们使用AWK”。现在,他们有两个问题。-D. Tilbrook

    这是一个玩笑,是一次真正的开掘,但它也是通过将正则表达式与其他不良解决方案链接起来来强调正则表达式的一种方式。这是一个伟大的哈哈,只有严肃的时刻。

  2. 对我来说(请注意,此报价是有意接受的解释)-含义很直接。仅仅宣布使用正则表达式的想法并不能解决问题。此外,通过添加其他规则与您所使用的语言不同的语言,您已经增加了代码的认知复杂性。

  3. 尽管这很有趣,但是您需要将非正则表达式解决方案的复杂性与正则表达式解决方案的复杂性以及包含正则表达式的其他复杂性进行比较。尽管添加正则表达式会产生额外费用,但解决正则表达式的问题可能还是值得的。


21

正则表达式现在或setoreador保留其他任何未格式化的内容;实际上,gexi可能更容易阅读此文本,但不幸的是,由于某些实现方式不允许格式化和让人生气的人不知道您可以这样做,因此它们具有很高的声誉。

(正则表达式的读取或维护并不比任何其他未格式化的内容差;确实,正则表达式可能比此处的这段文本更易于阅读-但不幸的是,它们的声誉很差,因为某些实现不允许格式设置,一般来说人们不知道你能做到。)


这是一个简单的例子:

^(?:[^,]*+,){21}[^,]*+$


无论如何,这实际上并不难于阅读或维护,但是当看起来像这样时,它甚至变得更加容易:

(?x)    # enables comments, so this whole block can be used in a regex.
^       # start of string

(?:     # start non-capturing group
  [^,]*+  # as many non-commas as possible, but none required
  ,       # a comma
)       # end non-capturing group
{21}    # 21 of previous entity (i.e. the group)

[^,]*+  # as many non-commas as possible, but none required

$       # end of string

那是一个最重要的示例(评论$类似于评论i++),但是显然,阅读,理解和维护它应该没有问题。


只要您清楚什么时候适合使用正则表达式以及什么时候不适合使用正则表达式,它们就没有错,而且大多数情况下JWZ引用实际上并没有适用。


1
可以,但是我不是在寻找正则表达式优点的讨论,而且我也不想看到这种讨论。我只是想了解他的意思。
Paul Biggar

1
然后,livibetter注释中的链接会告诉您您需要了解的内容。该响应只是指出正则表达式不需要晦涩难懂,因此引号是无稽之谈。
彼得·布顿

8
使用有*+什么意义?与just有*什么不同(功能上)?
Timwi,

1
尽管您说的可能是正确的,但它并不能回答这个特定问题。您的回答可以归结为“我认为报价通常不正确”。问题不是关于它是否正确,而是报价的含义。
布莱恩·奥克利

2
*+在这种情况下,这样做毫无意义。一切都是锚定的,并且可以通过最多可计数22个的自动机在一次通过中进行匹配。那些非逗号集上的正确修饰符只是普通的*。(此外,贪婪匹配算法和非贪婪匹配算法之间也应该没有区别。这是一个非常简单的情况。)
Donal Fellows 2013年


14

正则表达式非常强大,但是它们有一个小问题和一个大问题。它们很难写,几乎无法阅读。

在最佳情况下,使用正则表达式可以解决问题,因此,您仅会遇到复杂代码的维护问题。如果您没有正确地使用正则表达式,则既有原始问题,也有无法读取的代码问题。

有时将正则表达式称为只写代码。面对需要修复的正则表达式,从头开始通常比尝试理解该表达式要快。


1
真正的问题是正则表达式无法实现例如解析器,因为它们无法计算当前嵌套的深度。

4
@ThorbjørnRavn Andersen:这更多的是限制而不是问题。如果您尝试使用正则表达式只是一个问题,那么正则表达式就不成问题,方法选择就不成问题。
2011年

1
您可以对词法分析器使用RE(对于大多数语言而言)就很好了,但将令牌流组装到解析树(即parsing)中的形式正式地超出了它们。
Donal Fellows

10

问题是正则表达式是一个复杂的野兽,只有完美使用正则表达式才能解决问题。如果不这样做,最终会遇到两个问题:原始问题 正则表达式。

您声称它可以完成一百行代码的工作,但是您也可以提出这样的论据,即一百行清晰,简洁的代码比一行正则表达式更好。

如果您需要以下证明:您可以签出此SO Classic或仅通过SO Regex标签进行梳理


8
您第一句话中的任何说法都不正确。正则表达式并不是特别复杂,并且像其他任何工具一样,您也需要完全了解它才能解决问题。那只是FUD。您的第二段很荒谬:您当然可以提出争议。但这不是一个好人。
康拉德·鲁道夫2014年

1
@KonradRudolph我认为有很多正则表达式生成和验证工具的事实表明正则表达式一种复杂的机制。它不是人类可读的(通过设计),并且可能会导致某些人修改或编写使用正则表达式的代码而导致流程的完全改变。至于第二部分,我认为这很明显是因为P.SE上的知识丰富,并且说“调试代码的难度是编写它的两倍,因此,如果您编写最聪明的代码,则可以从定义上讲,还不足以调试它”
Ampt 2014年

2
那不是一个适当的论点。是的,请确保正则表达式很复杂。但是其他编程语言也是如此。正则表达式比大多数其他语言复杂得多,并且用于正则表达式的工具与其他语言的开发工具相形见F(FWIW,我广泛使用正则表达式,但我从未使用过此类工具……)。这是一个简单的事实,即使是复杂的正则表达式也比等效的非正则表达式解析代码更简单
康拉德·鲁道夫

@KonradRudolph我认为我们当时对简单一词的定义存在根本分歧。我会告诉您,正则表达式可以更高效或更强大,但是当您想到正则表达式时,任何人都不会想到这个简单的词。
Ampt

也许我们可以做到,但是我的定义是可行的:我的意思很简单,易于理解,易于维护,隐藏的bug少等。当然,乍看之下,复杂的正则表达式看起来并不十分可理解。但是,同样是真正的为等效的非正则表达式的代码。我从未说过正则表达式很简单。我是说它们更简单 –我正在比较。那很重要
康拉德·鲁道夫

7

含义包括两个部分:

  • 首先,您没有解决原始问题。
    这可能是指正则表达式通常不能提供常见问题的完整解决方案这一事实。
  • 其次,您现在增加了与选择的解决方案相关的其他难度。
    就正则表达式而言,附加困难可能是指复杂性,可维护性,或与使正则表达式适合本来无法解决的问题相关的附加困难。

7

当您在2014年提出要求时,与今天的环境相比,关注1997年环境的编程语言意识形态会很有趣。我不会在这里进行辩论,但是有关Perl和Perl本身的观点已经发生了很大变化。

但是,为了留在2013年(de l'eau acoulésous les ponts depuis),我建议着重使用著名的XKCD漫画重新引用报价,该漫画直接引用了Jamie Zawinski的漫画

XKCD上的一则有关正则表达式,Perl和问题的漫画

首先,我有问题要了解这个漫画,因为它是一个参考Zawinski撰写报价,一个Jay-Z的歌词的报价,以及 GNU的参考program --help -z标志2,所以,这是太多的文化,我理解它。

我知道这很有趣,但我当时真的不知道为什么。人们经常在开玩笑关于Perl和正则表达式的笑话,特别是因为它不是最时髦的编程语言,所以并不真正知道为什么它应该很有趣……也许是因为Perl贩子们在做些愚蠢的事情

因此,最初的报价似乎是基于现实生活中的问题(痛苦吗?就像锤子可能会伤害泥瓦匠一样,使用开发人员可能会伤害的工具(大脑,感觉)进行编程。有时,关于哪种工具最好的争论很多,但这几乎毫无用处,因为这是您的口味编程团队的口味文化经济原因的问题。关于此的另一本出色的XKCD漫画:

XKCD上的一本有关编程工具辩论的漫画

我能理解人们对正则表达式感到痛苦,他们确实相信另一种工具更适合正则表达式的设计目的。当@ karl-bielefeldt 以出色的表现力回答您的问题时,责任重大,而正则表达式对此尤为关注。如果开发人员不关心S-他如何处理正则表达式,那么最终将对以后维护代码的人来说将是一个痛苦。

我将通过引用引述重新制定引述来结束本答复,引述引述了Damian Conw ay的 Perl Best Practices(2005年一本书)中的一个典型示例。

他解释说,编写这样的模式:

m{'[^\\']*(?:\\.[^\\']*)*'}

... 比编写这样的程序更不可接受

sub'x{local$_=pop;sub'_{$_>=$_[0
]?$_[1]:$"}_(1,'*')._(5,'-')._(4
,'*').$/._(6,'|').($_>9?'X':$_>8
?'/':$")._(8,'|').$/._(2,'*')._(
7,'-')._(3,'*').$/}print$/x($=).
x(10)x(++$x/10).x($x%10)while<>;

但是它可以被重写虽然还不是很漂亮,但是至少现在是可以生存的。

# Match a single-quoted string efficiently...
m{ '            # an opening single quote
    [^\\']*     # any non-special chars (i.e., not backslash or single quote)
    (?:         # then all of...`
    \\ .        # any explicitly backslashed char
    [^\\']*     #    followed by any non-special chars
    )*          # ...repeated zero or more times
    '           # a closing single quote
}x

这种矩形代码不是可通过清晰,可维护和可读的方式格式化的则表达式的第二个问题。


2
/* Multiply the first 10 values in an array by 2. */ for (int i = 0 /* the loop counter */; i < 10 /* continue while it is less than 10 */; ++i /* and increment it by 1 in each iteration */) { array[i] *= 2; /* double the i-th element in the array */ }
5gon12eder'5

6

如果您应该从计算机科学中学到一件事,那就是乔姆斯基层次结构。我要说的是,正则表达式的所有问题都来自尝试用它解析上下文无关的语法。当您可以对CFG中的嵌套级别施加限制(或认为可以施加限制)时,您将获得那些冗长而复杂的正则表达式。


1
是! 在没有CS背景的情况下学习正则表达式的人并不总是了解,正则表达式在某些数学上是做不到的。
benzado 2011年

5

正则表达式比全面解析更适合于标记化。

但是,程序员需要解析的大量内容可以通过常规语言进行解析(或者更糟的是,可以通过常规语言进行解析,并且如果您只编写更多的代码……)。

因此,如果习惯于“啊哈,我需要将文本分开,我将使用正则表达式”,那么当您需要更接近下推式自动机,CFG解析器或更强大的语法 那通常以眼泪结束。

因此,我认为引述并不是打击正则表达式,而是使用它们(并且使用得很好,它们确实非常有用),但是过分依赖正则表达式(或者特别是对它们的不严格选择) 。


3

jwz的报价简直让他摇摇欲坠。正则表达式与任何语言功能都没有什么不同-容易搞砸,难以优雅地使用,有时功能强大,有时不适当,通常有据可查,并且经常有用。

对于浮点算术,闭包,面向对象,异步I / O或您可以命名的其他任何内容,也可以这样说。如果您不知道自己在做什么,那么编程语言会让您难过。

如果您认为正则表达式很难阅读,请尝试阅读等效的解析器实现以使用有问题的模式。正则表达式之所以会获胜,是因为它们比完整的解析器更紧凑...而且在大多数语言中,它们也更快。

请勿放任使用正则表达式(或任何其他语言功能),因为自我宣传的博客作者会发表不合格的声明。自己尝试一下,看看有什么适合您。


1
FWIW,浮点算法比RE 更加棘手,但看起来更简单。谨防!(至少棘手的RE看起来似乎很危险。)
Donal Fellows

3

我最喜欢的深入解答是著名的罗伯·派克(Rob Pike)在一篇博客文章中给出的,该文章摘自Google内部代码注释:http : //commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- and.html

总结是,并不是说它们很糟糕,而是它们经常用于不一定适合的任务,尤其是在词法分析和某些输入方面。

正则表达式很难编写,很难编写,并且相对于其他技术而言可能很昂贵。另一方面,Lexers相当容易正确编写(如果不是那么紧凑),并且非常易于测试。考虑查找字母数字标识符。编写正则表达式不是太难(像“ [a-ZA-Z _] [a-ZA-Z_0-9] *”之类),但是作为一个简单的循环写起来实际上并不难。但是,循环的性能将更高,并且涉及的代码更少。正则表达式库是一件大事。使用一个解析标识符就像使用法拉利去商店喝牛奶。

他说的远不止于此,他认为正则表达式在例如文本编辑器中模式的一次性匹配中很有用,但很少在编译后的代码中使用,等等。值得一读。


0

这与艾伦·佩里斯(Elan Perlis)的第34号诗句有关:

字符串是一个严格的数据结构,在传递它的任何地方都存在很多重复的过程。它是隐藏信息的理想工具。

因此,如果您选择字符串作为您的数据结构(自然而然,将基于正则表达式的代码作为操作它的算法),即使它可以工作,您也会遇到问题:围绕不合适的数据表示进行不良设计,这很难解决扩展,效率低下。

但是,它通常不起作用:原始问题无法解决,因此在这种情况下,您会遇到两个问题。


0

正则表达式广泛用于快速而肮脏的文本解析。它们是表达模式的好工具,比普通的字符串匹配要复杂一些。

但是,随着正则表达式变得越来越复杂,服务器问题引起了他们的注意。

  1. 正则表达式的语法已针对简单匹配进行了优化,大多数字符都与自己匹配。这对于简单的模式非常有用,但是一旦您完成了多个嵌套级别,您最终将看到的东西看起来更像是行噪,而不是结构良好的代码。我猜您可以将正则表达式编写为一系列串联的字符串,中间带有缩进和注释,以显示代码的结构,但实际上很少见到这种情况。
  2. 仅某些类型的文本匹配非常适合正则表达式。通常,您会发现自己得到了一种基于正则表达式的快速而又肮脏的解析器,用于某种标记语言,但是随后尝试覆盖更多的极端情况,发现正则表达式变得越来越复杂,可读性也越来越差
  3. 正则表达式的时间复杂度可能不高。最终得出一个模式,当它匹配时效果很好,但在某些不匹配的情况下具有O(2 ^ n)复杂度,这并不难。

因此,从文本处理问题开始,对其应用正则表达式并最终遇到两个问题(您试图解决的原始问题和试图解决(但不能正确解决)的正则表达式)很容易。原来的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.