Answers:
某些编程技术通常不为程序员所理解(正则表达式,浮点数,Perl,AWK,IoC ... 等)。
这些是解决正确问题集的强大工具。正则表达式对于匹配正则语言特别有用。问题的症结在于:很少有人知道如何描述常规语言(它是计算机科学理论/语言学的一部分,它使用有趣的符号-您可以在Chomsky层次结构中进行了解)。
处理这些问题时,如果错误使用它们,就不可能真正解决您原来的问题。使用正则表达式来匹配HTML(这种情况太常见了)将意味着您将错过边缘情况。现在,您仍然遇到了尚未解决的原始问题,并且通过使用错误的解决方案引入了另一个细微的错误。
这并不是说不应该使用正则表达式,而是应该努力理解正则表达式可以解决的问题以及不能明智地解决和使用的问题。
维护软件的关键是编写可维护的代码。使用正则表达式可能会违反该目标。在使用正则表达式时,您已经以特定的领域特定语言编写了小型计算机(特别是非确定性有限状态自动机)。用这种语言编写“ Hello world”等价物并获得基本的信任很容易,但是需要对常规语言的理解来限制进一步发展,以避免编写可能很难识别和修复的其他错误(因为它们不是正则表达式所在程序的一部分)。
因此,现在您遇到了一个新问题;您选择了正则表达式的工具来解决它(当它不合适时),现在您有两个bug,因为它们隐藏在另一层抽象中,所以很难找到它们。
正则表达式-特别是非琐碎的正则表达式-可能难以编码,理解和维护。您只需要查看标记[regex]
为Stack Overflow的问题数,发问者认为问题的答案是正则表达式,并且随后被卡住了。在很多情况下,可以(也许应该)以不同的方式解决问题。
这意味着,如果您决定使用正则表达式,则现在有两个问题:
基本上,我认为他的意思是,如果没有其他解决问题的方法,则只应使用正则表达式。另一个解决方案可能会更易于编码,维护和支持。它可能会变慢或降低效率,但是如果不是很关键,那么维护和支持的简便性应该是首要考虑的问题。
尽管有一定道理,但这主要是开玩笑的笑话。
对于某些任务,正则表达式非常适合。我曾经用一个正则表达式替换了500行手动编写的递归下降解析器代码,而这大约需要10分钟才能完全调试。人们说正则表达式很难理解和调试,但是适当应用的正则表达式几乎不像手工设计的大型解析器那么难调试。在我的示例中,调试非正则表达式解决方案的所有边缘情况花了两个星期。
但是,对本叔叔的解释是:
表现力强,责任重大。
换句话说,正则表达式为您的语言增加了表达能力,但这使程序员更有责任为给定任务选择最易读的表达方式。
对于正则表达式,有些事情一开始看起来很不错,但事实并非如此。例如,任何带有嵌套标记的东西,例如HTML。当更简单的方法更清晰时,有时人们使用正则表达式。例如,string.endsWith("ing")
比等效的正则表达式更容易理解。有时,人们试图将一个大问题塞入一个正则表达式中,在这种情况下,将其分解为更合适的方法。有时人们无法创建适当的抽象,一遍又一遍地重复一个正则表达式,而不是创建一个功能强大的函数来完成相同的工作(也许是在正则表达式内部实现的)。
由于某种原因,正则表达式有一种怪异的趋势,可为常规软件工程原理(如单一职责和DRY)造成盲点。这就是为什么即使是爱他们的人有时也会发现他们有问题的原因。
杰夫·阿特伍德(Jeff Atwood)在讨论此报价的博客文章中提出了另一种解释:正则表达式:现在您有两个问题 (感谢链接的欣快感)
通过分析原始1997年主题中Jamie的帖子全文,我们发现以下内容:
Perl的本性鼓励使用正则表达式,几乎可以排除所有其他技术。它们是从点A到点B的最“明显”(至少对于不了解的人来说)最遥远的方式。
第一个引号太浮夸了,不能被认真对待。但是,我完全同意。杰米(Jamie)试图说明的重点是:正则表达式本身并不是邪恶的,而是正则表达式的过度使用是邪恶的。
即使你不完全了解的正则表达式,你碰上金锤子问题,试图解决使用正则表达式,出了问题时,它会更容易和更清晰的做常规代码同样的事情(见CodingHorror:正则表达式使用与Regex滥用)。
还有另一篇关于报价内容的博客文章,比Atwood更为详细:Jeffrey Friedl的博客:著名的“现在有两个问题”报价的来源
这个报价有几件事情发生。
该报价是较早笑话的重述:
每当遇到问题时,有人会说“让我们使用AWK”。现在,他们有两个问题。-D. Tilbrook
这是一个玩笑,是一次真正的开掘,但它也是通过将正则表达式与其他不良解决方案链接起来来强调正则表达式的一种方式。这是一个伟大的哈哈,只有严肃的时刻。
对我来说(请注意,此报价是有意接受的解释)-含义很直接。仅仅宣布使用正则表达式的想法并不能解决问题。此外,通过添加其他规则与您所使用的语言不同的语言,您已经增加了代码的认知复杂性。
尽管这很有趣,但是您需要将非正则表达式解决方案的复杂性与正则表达式解决方案的复杂性以及包含正则表达式的其他复杂性进行比较。尽管添加正则表达式会产生额外费用,但解决正则表达式的问题可能还是值得的。
正则表达式现在或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引用实际上并没有适用。
*+
什么意义?与just有*
什么不同(功能上)?
*+
在这种情况下,这样做毫无意义。一切都是锚定的,并且可以通过最多可计数22个的自动机在一次通过中进行匹配。那些非逗号集上的正确修饰符只是普通的*
。(此外,贪婪匹配算法和非贪婪匹配算法之间也应该没有区别。这是一个非常简单的情况。)
正则表达式非常强大,但是它们有一个小问题和一个大问题。它们很难写,几乎无法阅读。
在最佳情况下,使用正则表达式可以解决问题,因此,您仅会遇到复杂代码的维护问题。如果您没有正确地使用正则表达式,则既有原始问题,也有无法读取的代码问题。
有时将正则表达式称为只写代码。面对需要修复的正则表达式,从头开始通常比尝试理解该表达式要快。
问题是正则表达式是一个复杂的野兽,只有完美使用正则表达式才能解决问题。如果不这样做,最终会遇到两个问题:原始问题 和正则表达式。
您声称它可以完成一百行代码的工作,但是您也可以提出这样的论据,即一百行清晰,简洁的代码比一行正则表达式更好。
如果您需要以下证明:您可以签出此SO Classic或仅通过SO Regex标签进行梳理
当您在2014年提出要求时,与今天的环境相比,关注1997年环境的编程语言意识形态会很有趣。我不会在这里进行辩论,但是有关Perl和Perl本身的观点已经发生了很大变化。
但是,为了留在2013年(de l'eau acoulésous les ponts depuis),我建议着重使用著名的XKCD漫画重新引用报价,该漫画直接引用了Jamie Zawinski的漫画:
首先,我有问题要了解这个漫画,因为它是一个参考Zawinski撰写报价,和一个Jay-Z的歌词的报价,以及 GNU的参考program --help -z
标志2,所以,这是太多的文化,我理解它。
我知道这很有趣,但我当时真的不知道为什么。人们经常在开玩笑关于Perl和正则表达式的笑话,特别是因为它不是最时髦的编程语言,所以并不真正知道为什么它应该很有趣……也许是因为Perl贩子们在做些愚蠢的事情。
因此,最初的报价似乎是基于现实生活中的问题(痛苦吗?就像锤子可能会伤害泥瓦匠一样,使用开发人员可能会伤害的工具(大脑,感觉)进行编程。有时,关于哪种工具最好的争论很多,但这几乎毫无用处,因为这是您的口味或编程团队的口味,文化或经济原因的问题。关于此的另一本出色的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
这种矩形代码不是可通过清晰,可维护和可读的方式格式化的正则表达式的第二个问题。
/* 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 */ }
如果您应该从计算机科学中学到一件事,那就是乔姆斯基层次结构。我要说的是,正则表达式的所有问题都来自尝试用它解析上下文无关的语法。当您可以对CFG中的嵌套级别施加限制(或认为可以施加限制)时,您将获得那些冗长而复杂的正则表达式。
jwz的报价简直让他摇摇欲坠。正则表达式与任何语言功能都没有什么不同-容易搞砸,难以优雅地使用,有时功能强大,有时不适当,通常有据可查,并且经常有用。
对于浮点算术,闭包,面向对象,异步I / O或您可以命名的其他任何内容,也可以这样说。如果您不知道自己在做什么,那么编程语言会让您难过。
如果您认为正则表达式很难阅读,请尝试阅读等效的解析器实现以使用有问题的模式。正则表达式之所以会获胜,是因为它们比完整的解析器更紧凑...而且在大多数语言中,它们也更快。
请勿放任使用正则表达式(或任何其他语言功能),因为自我宣传的博客作者会发表不合格的声明。自己尝试一下,看看有什么适合您。
我最喜欢的深入解答是著名的罗伯·派克(Rob Pike)在一篇博客文章中给出的,该文章摘自Google内部代码注释:http : //commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- and.html
总结是,并不是说它们很糟糕,而是它们经常用于不一定适合的任务,尤其是在词法分析和某些输入方面。
正则表达式很难编写,很难编写,并且相对于其他技术而言可能很昂贵。另一方面,Lexers相当容易正确编写(如果不是那么紧凑),并且非常易于测试。考虑查找字母数字标识符。编写正则表达式不是太难(像“ [a-ZA-Z _] [a-ZA-Z_0-9] *”之类),但是作为一个简单的循环写起来实际上并不难。但是,循环的性能将更高,并且涉及的代码更少。正则表达式库是一件大事。使用一个解析标识符就像使用法拉利去商店喝牛奶。
他说的远不止于此,他认为正则表达式在例如文本编辑器中模式的一次性匹配中很有用,但很少在编译后的代码中使用,等等。值得一读。
正则表达式广泛用于快速而肮脏的文本解析。它们是表达模式的好工具,比普通的字符串匹配要复杂一些。
但是,随着正则表达式变得越来越复杂,服务器问题引起了他们的注意。
因此,从文本处理问题开始,对其应用正则表达式并最终遇到两个问题(您试图解决的原始问题和试图解决(但不能正确解决)的正则表达式)很容易。原来的问题。