何时最好使用正则表达式而不是基本的字符串拆分/子字符串?

23

在我需要使用字符串的一部分、有关该字符串的信息等情况下,似乎经常会遇到使用字符串解析和正则表达式之间的选择。

出现这种情况的原因是我们正在评估soap标头的操作,在通过WCF的OperationContext对象将其解析为可管理的内容后之后对其进行决策。目前,简单的子字符串解析似乎是保持实现简单的简单解决方案,但我想知道正则表达式是否更好或更健壮。同时,我也在想是否在我们特定的情况下,这样做就像是用猎枪打苍蝇。

因此,我不得不问,人们在决定使用正则表达式还是典型的字符串解析时使用的典型阈值是什么?请注意,我对正则表达式不太熟悉,因此除非绝对必要,否则我会回避使用它,以避免引入比我需要的更多的复杂性。

如果您不能从我的缩写选择中看出来,这是在.NET(C#)领域进行的,但我认为这与问题无关。


编辑:根据我的典型Raybell风格,我的问题太冗长或误导了。我要道歉。我是为了提供一些背景信息来帮助寻找线索,而不是误导人们。

基本上,我正在寻找何时使用子字符串及其变体,何时使用正则表达式以及反之的指南。虽然有些答案可能忽略了这一点(再次,我的错),但我真诚地感激它们并相应地点赞。


除非您有其他要求,否则我认为此问题回答了与您提出的相同的问题:https://dev59.com/PUXRa4cB1Zd3GeqPpBxQ - EBGreen
这很接近我想要的。虽然我进行了搜索,但从未找到符合条件的内容,尽管这似乎和任何东西一样接近。 - Steven Raybell
我想问的是,那个问题是否给了你想要的信息? - EBGreen
有点类似,但是我下面有更好的答案,很快就会采纳它。 - Steven Raybell
7个回答

26

我的主要准则是在编写临时代码和用户输入验证时使用正则表达式。或者当我试图在一大段文本中查找特定模式的时候。对于其他大多数目的,我会编写语法并实现简单的解析器。

一个重要的准则(虽然我看到很多人尝试规避它)是在目标语言的语法是递归的情况下,总是使用解析器。

例如,考虑一个用于评估带括号算术表达式的小型“表达式语言”。这种语言中的“程序”示例如下:

1 + 2
5 * (10 - 6)
((1 + 1) / (2 + 2)) / 3

语法非常容易编写,看起来像这样:

DIGIT := ["0"-"9"]
NUMBER := (DIGIT)+
OPERATOR := ("+" | "-" | "*" | "/" )
EXPRESSION := (NUMBER | GROUP) (OPERATOR EXPRESSION)?
GROUP := "(" EXPRESSION ")"

利用该语法,您可以轻松构建递归下降解析器。

等价的正则表达式确实很难编写,因为正则表达式通常不支持递归。

另一个很好的例子是JSON数据处理。我曾见过有人试图使用正则表达式消耗JSON数据,但这是疯狂的。JSON对象是递归的,因此它们非常适合使用正则语法和递归下降解析器。


看了其他人的回答后,我觉得我可能回答错了问题。

我把问题理解为“何时应该使用简单的正则表达式而不是完整的解析器?”,而大多数人似乎将问题理解为“何时应该自己编写笨拙的、基于字符的验证方案,而不是使用正则表达式?”

如果按照那种解释,我的答案是:永远不要。


好吧... 再修改一次。

我会对自定义的方案更加宽容。只是... 不要称其为“解析” :o)

我认为一个好的经验法则是,只有在能够使用单个谓词实现所有逻辑的情况下,才应该使用字符串匹配原语。例如:

if (str.equals("DooWahDiddy")) // No problemo.

if (str.contains("destroy the earth")) // Okay.

if (str.indexOf(";") < str.length / 2) // Not bad.

一旦你的条件包含多个谓词,那么你就开始发明自己的临时字符串验证语言,你可能应该勇敢地学习一些正则表达式。

if (str.startsWith("I") && str.endsWith("Widget") &&
    (!str.contains("Monkey") || !str.contains("Pox")))  // Madness.

正则表达式其实并不难学习。与 C# 这样拥有数十个关键字、基础类型和运算符、以及成千上万个类的完整语言相比,正则表达式绝对是非常简单的。大多数正则表达式实现支持约十几个操作(多或少不等)。

以下是一个很好的参考资料:

http://www.regular-expressions.info/

顺带一提,如果你确实想要学习编写自己的解析器(使用 lex/yacc、ANTLR、JavaCC 或其他类似工具),那么学习正则表达式是一个很好的准备,因为解析器生成器工具使用了许多相同的原则。


我并不一定要进行逐个字符的验证。我只是想获取一个子字符串,然后对其进行操作。总的来说,我想知道在选择子字符串和正则表达式之间的一般准则是什么。我相信我在我的问题中可能没有表达清楚... - Steven Raybell
跟你一样,我也支持“真正的解析器”的想法——为什么人们对语法这么害怕? - Draemon
对我来说,情况恰恰相反。我对于阅读庞大的企业框架无穷无尽的API文档感到厌倦,但是当我使用新概念、算法和数学技巧解决棘手问题时,我会感到非常兴奋。我认为自己更像一个“计算机科学家”,而不是一个“软件工程师”。 - benjismith
对于解析器,您会推荐哪些工具作为一个好的起点?我已经做了一些基本的东西,但从未真正深入涉及任何复杂的内容。 - Steven Raybell
在Java中,我最喜欢的工具是JavaCC。如果你有正则表达式背景,它相当容易学习,并且也非常强大。但对于其他平台(或多平台支持),你无法击败ANTLR。它有点更复杂和难以学习,但它真的非常强大。 - benjismith
显示剩余4条评论

7
正则表达式有以下优点:
  • 更易于理解
  • 更清晰地表达意图
  • 代码更加简洁
  • 更容易进行更改/适应
在某些情况下,使用正则表达式可以实现所有这些优点,而在其他情况下,则只能实现其中的一些(例如,正则表达式并不易于理解),在另一些情况下,正则表达式更难理解、混淆了意图、代码更长且难以更改。
从正则表达式中获得的优点越多(可能还有其他优点),我就越可能使用它们。
一个可能的经验法则:如果理解正则表达式需要几分钟时间才能为那些对正则表达式稍有了解的人所理解,那么你不想使用它(除非“正常”代码更加复杂;-)。
嗯...仍然没有简单的经验法则,抱歉。

3

我们正在评估SOAP头部的操作,并根据此做出决策。

永远不要使用正则表达式或基本字符串分析来处理XML。目前每种常用语言都有完美的XML支持。XML是一个看起来简单实际上非常复杂的标准,很难保证您的代码能够正确解析所有格式良好的XML输入,即使它能够这样做,也会浪费时间,因为(正如刚才提到的)每种常用语言都有XML支持。使用正则表达式解析XML是不专业的。

总体而言,应该尽量减少使用正则表达式,因为它们不易阅读。通常情况下,您可以将字符串解析和正则表达式组合(可能在循环中),以创建比仅使用正则表达式更简单的解决方案。


我在这里有点误导,我很抱歉。事实上,当我们处理这个问题时,它已经通过OperationContext被解析了。不过还是感谢你指出了这一点! - Steven Raybell
我稍微修改了一下问题以提高清晰度,但在我看来它仍然有些混淆。我会在有更多时间时再重新构思一下。对此我感到抱歉。 - Steven Raybell
抱歉,我可能应该更有礼貌一些,但每次看到这种情况时,我都感到非常恼火。 - Tmdean
没关系!我完全理解你的感受。选择正确的工具才能做好工作。没必要重复造轮子,或者说是解析器。 - Steven Raybell

3
我同意benjismith所说的,但我想再详细说明一下。对于非常简单的语法,基本字符串解析可以很好地工作,正则表达式也可以。我不认为它们过度了。如果它有效,就使用最简单的方法。对于中等到复杂的字符串解析,通常使用正则表达式。
但是,一旦您发现自己需要定义语法,即进行复杂的字符串解析,请尽快回到使用某种有限状态机或类似物。正则表达式并不适用于大规模应用,这个术语使用得比较宽泛。它们变得复杂,难以解释,甚至无能为力。
我见过至少一个项目,其中正则表达式的使用不断增长,很快他们就难以插入新功能。当最终到达进行新的主要版本发布时,他们放弃了所有正则表达式,并采用了语法分析器的路线。

在这里的一个案例中,我曾经看到一个正则表达式实际上会因为正确的输入而递归循环。这导致服务器CPU飙升并允许DOS攻击发生。因此,毋庸置疑,当我看到它们作为解决方案时,我会非常谨慎,原因就在于此。 - Steven Raybell

1

当您所需的转换不是基本的——但在概念上仍然很简单。

例如,如果您只是进行直接字符串替换,则没有理由使用正则表达式...使用string.Replace会更容易

另一方面,如果有许多条件或特殊情况需要超过50个字符的正则表达式才能实现的复杂规则,则如果您不明确编写它,以后维护起来可能会非常困难。


0

除非是像拆分逗号分隔字符串这样的非常简单的事情,否则我总是会使用正则表达式。如果我认为字符串可能会变得更加复杂,我可能会从正则表达式开始。

我不认同正则表达式很难或复杂的观点。这是每个开发人员都应该学习和掌握的一种工具。它们有无数的用途,一旦学会了,这就是你永远不必再担心的事情。

正则表达式很少过度 - 如果匹配很简单,那么正则表达式也很简单。


即使是一个 CSV 解析器,由于引号规则,编写起来也是非常复杂的。(换行符和逗号都可以出现在单个字段中,只要该字段被引号包围。)不要小看这个谦虚的 CSV!!!即使使用正则表达式,正确解析它也是非常困难的 :o) - benjismith
我说的是逗号分隔的字符串,而不是CSV文件。对于CSV文件,我永远不会推荐除了专用库或解析器之外的任何东西。实际上,我曾经编写过一个C++ CSV解析器,它可以处理以上所有情况,但我的父亲是一个DFA。 - Draemon

0
我认为判断何时使用正则表达式以及何时不使用最简单的方法是当您的字符串搜索需要IF / THEN语句或任何类似于此或那种逻辑时,您需要比简单的字符串比较更好的东西,这就是正则表达式发挥作用的地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接