在Java中什么时候值得使用RegEx?

4

我正在编写一个小应用程序,它会读取一些输入并根据该输入执行某些操作。

目前我正在寻找以"magic"结尾的行,我将使用String的endsWith方法。对于阅读我的代码的人来说,这是非常清晰明了的。

另一种方法是创建一个Pattern并尝试匹配以"magic"结尾的行。这也很清楚,但我个人认为这是一种过度设计,因为我要查找的模式并不复杂。

你认为什么时候值得使用Java正则表达式?如果是复杂性问题,你个人如何定义什么是足够复杂的?

此外,有没有使用Pattern比字符串操作更快的情况?

编辑:我正在使用Java 6。


3
固定文本操作通常比正则表达式快。不过,除非你处理的是成千上万行的输入,否则我建议选择编码更容易的方式。 - Paul Tomblin
+1 因为第一个关注性能。谢谢。 - Russell
11个回答

10

基本上:如果有一个非正则表达式操作可以一步完成你想要的事情,那么请始终选择这种方法。

这与性能关系不大,而是与a)易读性和b)编译时安全性有关。专门的非正则表达式版本通常比正则表达式版本更容易阅读。在这些专门的方法中,一个打字错误将无法编译,而在正则表达式中,一个打字错误将在运行时失败。

比较基于正则表达式的解决方案和非基于正则表达式的解决方案

String s = "Magic_Carpet_Ride";

s.startsWith("Magic");   // non-regex
s.matches("Magic.*");    // regex

s.contains("Carpet");    // non-regex
s.matches(".*Carpet.*"); // regex

s.endsWith("Ride");      // non-regex
s.matches(".*Ride");     // regex

在这些情况下,使用非正则表达式版本是很明显的选择。

但当事情变得更加复杂时,就要看情况而定。我想我仍然会在以下情况下坚持使用非正则表达式,但许多人不会:

// Test whether a string ends with "magic" in any case,
// followed by optional white space
s.toLowerCase().trim().endsWith("magic"); // non-regex, 3 calls
s.matches(".*(?i:magic)\\s*");            // regex, 1 call, but ugly

针对 RegexesCanCertainlyBeEasierToReadThanMultipleFunctionCallsToDoTheSameThing 的回应:

我仍然认为非正则表达式版本更易读,但我会这样写:

s.toLowerCase()
 .trim()
 .endsWith("magic");

这可真是有很大的区别啊,不是吗?


3
换句话说,"只有在需要使用正则表达式时才应该使用它们"。 - Bryan Oakley
1
正则表达式可以比多个函数调用更易于阅读,却能实现同样的功能。 - tchrist
.* 是不必要的。在你使用它的所有地方。 - Mark Thomas
^$ 是不必要的; matches() 总是表现得像正则表达式在两端都被锚定一样。此外,请注意我对您上一个正则表达式的更正。(?i:magic) 不区分大小写地匹配 "magic",而 (?i) 打开整个正则表达式的无大小写模式(或者直到用 (?-i) 关闭)。 (?:i) 只是一个普通的非捕获组,匹配 "i"。 - Alan Moore
@seanizer,你是正确的。 .* 与 ^ 和 $ 是多余的,但 @alan 正确地指出了后者是不必要的。 - Mark Thomas
根据Alan Moore的建议调整了帖子。 - Sean Patrick Floyd

3

当使用String类的常规操作不能优雅地从字符串中获取所需内容时,您可以使用正则表达式。

一个很好的指示是当您开始拆分字符串,然后拆分这些结果,再拆分这些结果时。代码变得笨重。两行Pattern / Regex代码可以整理这个问题,并包装在一个经过单元测试的方法中。


@mark 尽可能多地提及它。正则表达式非常适合单元测试模式,代码简洁,测试也简洁......快乐的土地.... - hvgotcodes

2

任何可以使用正则表达式完成的任务也可以手工编码实现。

如果:

  1. 手动完成需要花费更多精力,而且效益不高。
  2. 您可以轻松地为您的任务想出一个正则表达式。

请勿使用正则表达式,如果:

  1. 有其他简单易行的方法可以完成,例如您的示例。
  2. 您正在解析的字符串不适合使用正则表达式。(通常会链接到此问题

1
可能习惯于链接到那个问题,但链接到这个答案也应该成为惯例,也许还应该链接到这个答案。我真的很厌倦人们毫无意识地重复错误说法,即现代模式仅限于古老的有限自动机可以处理的不规则命名的正则语言。 - tchrist
@tchrist:我从未提及关于正则语言的任何内容。我只是说,“如果有其他简单的方式来完成,就不要使用正则表达式”,在HTML这样的语言中这是正确的-你自己编写一个基于状态机的解析器比试图设计一个极其复杂的正则表达式来完成它要简单得多。 - casablanca

1

我认为你最好使用endsWith。除非你的要求发生改变,否则它更简单易懂,而且可能执行速度更快。

如果还有一些复杂性,例如你想匹配"magic"、"majik",但不匹配"Magic"或"Majik";或者你想匹配"magic"后跟一个空格,然后是一个单词,如"... magic spoon",但不匹配"...magic soup spoon",那么我认为正则表达式会是更好的选择。


0

如果您熟悉正则表达式的工作原理,您很快就会发现许多问题可以通过使用正则表达式轻松解决。

个人而言,如果使用Java字符串操作很容易解决问题,但如果您开始拆分字符串并对其进行子字符串操作,我会开始考虑正则表达式。

而且,如果您使用正则表达式,为什么要止步于行呢?通过配置您的正则表达式,您可以轻松地在一个正则表达式中读取整个文件(将Pattern.DOTALL作为参数传递给Pattern.compile,并且您的正则表达式不以换行符结尾)。我会将其与Apache Commons IOUtils.toString()方法相结合,这样您就可以快速处理一些非常强大的东西。

如果需要,我甚至会使用正则表达式来解析一些XML。(例如在单元测试中,我想检查XML中是否存在某些元素)。

例如,从我的某个单元测试中:

Pattern pattern = Pattern.compile(
                "<Monitor caption=\"(.+?)\".*?category=\"(.+?)\".*?>"
                + ".*?<Summary.*?>.+?</Summary>"
                + ".*?<Configuration.*?>(.+?)</Configuration>"
                + ".*?<CfgData.*?>(.+?)</CfgData>", Pattern.DOTALL);

这将匹配此 XML 中的所有段,并挑选出我想要进行一些子匹配的一些段。


0

如果你需要进行复杂的解析且需要生成大量对象,使用正则表达式会更加高效,因为这种方法考虑了计算能力和编写代码所需的智力。如果你身边有一个正则表达式专家,那么几乎总是值得的。因为模式很容易调整以适应业务规则的变化,而不需要进行主要的循环重构,这对于使用纯Java执行一些复杂任务的情况来说是必要的。


0
有一句话说:
当一些人遇到问题时,他们会想:“我知道了,我会使用正则表达式。”现在他们有两个问题了。 (link)。
对于一个简单的测试,我会像你所做的那样进行。如果发现变得更加复杂,那么只有在没有其他方法的情况下才会考虑使用正则表达式。

“随时都可以”?我不这么认为。那是断章取义。 - Mark Thomas
@Mark:你说得对,我误引用了,所以我改了文本。 - NotMe
你仍然引用得非常不正确。 :D 我可能已经厌倦了看到那段引语,但我仍然钦佩它的经典措辞。早期版本归因于D. Tilbrook的措辞远没有这么好。 - Alan Moore
@Alan:第三次就是成功的机会? - NotMe

0

如果您的基本行尾每次都相同,例如使用“magic”,那么最好使用endsWith。

但是,如果您有一行具有相同基础但可以具有多个值的行,例如:

<string> <number> <string> <string> <number>

其中字符串和数字可以是任何内容,那么最好使用RegEx。

您的行总是以字符串结尾,但您不知道该字符串是什么。


0
如果只是简单的以endsWith、startsWith或contains结尾、开头或包含,那么你应该使用这些函数。如果你正在处理更“复杂”的字符串,并且想要从这些字符串中提取信息,则可以使用regexp/matchers。
如果你有像“commandToRetrieve someNumericArgs someStringArgs someOptionalArgs”这样的东西,那么regexp会大大简化你的任务 :)

0

如果有更简单的方法,我永远不会在Java中使用正则表达式,就像在这种情况下使用endsWith方法一样。在Java中,正则表达式非常丑陋,可能只有String上的match方法是个例外。

通常避免使用正则表达式可以使您的核心更易读,也更容易让其他程序员理解。相反,复杂的正则表达式可能会混淆即使是最有经验的黑客。

至于性能问题:请进行分析。特别是在Java中。


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