27

我刚刚看到一个巨大的 Java 正则表达式,它让我对一般正则表达式的可维护性有所思考。我相信大多数人——除了一些糟糕的 perl 贩子——都会同意正则表达式很难维护。

我正在考虑如何解决这种情况。到目前为止,我最有希望的想法是使用流畅的界面。举个例子,而不是:

Pattern pattern = Pattern.compile("a*|b{2,5}");

可以写这样的东西

import static util.PatternBuilder.*

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

Pattern alternative = 
  or(
    string("a").anyTimes(),
    string("b").times(2,5)
  )
  .compile();

在这个非常简短的示例中,创建正则表达式的常用方法对于任何有才华的平庸开发人员来说仍然很容易阅读。然而,想想那些用 80 个字符填满两行或更多行的诡异表达式。当然,(冗长的)流畅的界面需要几行而不是两行,但我相信它会更具可读性(因此可维护)。

现在我的问题:

  1. 你知道正则表达式的任何类似方法吗?

  2. 你同意这种方法比使用简单的字符串更好吗?

  3. 您将如何设计 API?

  4. 你会在你的项目中使用这样一个简洁的实用程序吗?

  5. 你认为这会很有趣吗?;)

编辑: 想象一下,可能存在比我们都没有从正则表达式中获得的简单构造更高级别的方法,例如

// matches aaaab@example.com - think of it as reusable expressions
Pattern p = string{"a").anyTimes().string("b@").domain().compile();

编辑 - 评论的简短摘要:

有趣的是,大多数人认为正则表达式会继续存在——尽管需要工具来阅读它们,聪明的人会想办法让它们可维护。虽然我不确定流畅的界面是最好的方法,但我相信一些聪明的工程师 - 我们?;) - 应该花一些时间让正则表达式成为过去 - 他们已经和我们一起工作了 50 年就足够了,你不觉得吗?

开放赏金

赏金将奖励给正则表达式新方法的最佳创意(无需代码)。

编辑 - 一个很好的例子:

这是我正在谈论的那种模式 -对第一个能够翻译它的人的额外荣誉 - RegexBuddies 允许(它来自 Apache 项目顺便说一句)授予chiimez的额外荣誉:这是一个符合 RFC 的电子邮件地址验证模式 -尽管它的RFC822(参见ex-parrot.com),而不是5322 - 不确定是否有区别 - 如果是,我将奖励下一个额外的荣誉,以获得适合 5322 的补丁;)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";
4

19 回答 19

19

Martin Fowler 提出了另一种策略。即取正则表达式的有意义的部分并用变量替换它们。他使用以下示例:

 "^score\s+(\d+)\s+for\s+(\d+)\s+nights?\s+at\s+(.*)" 

变成

   String scoreKeyword = "^score\s+";
   String numberOfPoints = "(\d+)";
   String forKeyword = "\s+for\s+";
   String numberOfNights = "(\d+)";
   String nightsAtKeyword = "\s+nights?\s+at\s+";
   String hotelName = "(.*)";

   String pattern = scoreKeyword + numberOfPoints +
      forKeyword + numberOfNights + nightsAtKeyword + hotelName;

这更具可读性和可维护性。

上一个示例的目标是从字符串列表中解析 numberOfPoints、numberOfNights 和 hotelName,例如:

score 400 for 2 nights at Minas Tirith Airport
于 2009-10-16T20:52:06.107 回答
18

对于没有任何正则表达式经验的人来说可能会稍微容易一些,但是在某人学习了您的系统之后,他仍然无法在其他地方阅读正常的正则表达式。

另外,我认为您的版本对于正则表达式专家来说更难阅读。

我建议像这样注释正则表达式:

Pattern pattern = Pattern.compile(
  "a*     # Find 0 or more a        \n" +
  "|      # ... or ...              \n" +
  "b{2,5} # Find between 2 and 5 b  \n",
Pattern.COMMENTS);

这对于任何经验水平都很容易阅读,对于没有经验的人,它同时教授正则表达式。此外,可以根据情况定制注释,以解释正则表达式背后的业务规则,而不仅仅是结构。

此外,像RegexBuddy这样的工具可以获取您的正则表达式并将其翻译成:

匹配下面的正则表达式(仅在这个失败时尝试下一个替代)«a*»
   匹配字符“a”字面意思«a*»
      在零次和无限次之间,尽可能多次,根据需要回馈(贪婪)«*»
或者匹配下面的正则表达式 2(如果这个匹配失败,则整个匹配尝试失败)«b{2,5}»
   匹配字符“b”的字面意思«b{2,5}»
      2 到 5 次,尽可能多次,根据需要回馈(贪婪)«{2,5}»
于 2009-10-16T16:58:25.617 回答
10

这是一个有趣的概念,但正如它所呈现的那样,存在一些缺陷。

但首先回答的关键问题是:

现在我的问题:

1.你知道正则表达式的类似方法吗?

没有一个还没有被提及。我通过阅读问题和答案发现了这些。

2. 你同意这种方法比使用简单的字符串更好吗?

如果它像宣传的那样工作,它肯定会让事情更容易调试。

3. 你会如何设计 API?

请参阅下一节中的笔记。我以您的示例和链接的 .NET 库为起点。

4. 你会在你的项目中使用这样一个简洁的工具吗?

未定。使用当前版本的神秘正则表达式没有问题。而且我需要一个工具来将现有的正则表达式转换为流利的语言版本。

5. 你认为这会很有趣吗?;)

与编写实际代码相比,我更喜欢研究更高层次的方法。这解释了这个答案的文字墙。


以下是我注意到的几个问题,以及我将如何处理它。

结构不清晰。

您的示例似乎通过连接到字符串来创建正则表达式。这不是很健壮。我认为应该将这些方法添加到 String 和 Patern/Regex 对象中,因为它会使实现和代码更清晰。此外,它类似于正则表达式的经典定义方式。

仅仅因为我看不到它以任何其他方式工作,我对提议方案的其余注释将假定所有方法都作用于并返回 Pattern 对象。

编辑:我似乎一直使用以下约定。所以我已经澄清了它们并将它们移到这里。

  • 实例方法:模式增强。例如:捕获、重复、环顾断言。

  • 运算符:运算顺序。交替,串联

  • 常量:字符类、边界(代替 \w、$、\b 等)

如何处理捕获/聚类?

捕获是正则表达式的重要组成部分。

我看到每个 Pattern 对象在内部存储为一个集群。(?:模式)在 Perl 术语中。允许模式标记轻松混合和混合,而不会干扰其他部分。

我希望捕获作为模式上的实例方法来完成。取一个变量来存储匹配的字符串[s]。

pattern.capture(variable)将模式存储在变量中。如果捕获是要多次匹配的表达式的一部分,则变量应包含所有模式匹配的字符串数组。

流利的语言可能非常模棱两可。

流利的语言不太适合正则表达式的递归性质。所以需要考虑操作的顺序。仅仅将方法链接在一起不允许非常复杂的正则表达式。正是这种工具有用的情况。

Pattern pattern = string("a").anyTimes().or().string("b").times(2,5).compile();

生产/a*|b{2,5}//(a*|b){2,5}/

这样的方案将如何处理嵌套交替?例如:/a*|b(c|d)|e/

我看到了三种在正则表达式中处理交替的方法

  1. 作为运营商:pattern1 or pattern2 => pattern # /pattern1|pattern2/
  2. 作为类方法:Pattern.or( pattern1, pattern2[, pattern3]*) => pattern # /pattern1|patern2|patern3|...|/
  3. 作为实例方法:pattern1.or(pattern2) => pattern # /pattern1|patern2/

我会以同样的方式处理连接。

  1. 作为运营商:pattern1 + pattern2 => pattern # /pattern1pattern2/
  2. 作为类方法:Pattern.concatenate( pattern1, pattern2[, pattern3]*) => pattern # /pattern1patern2patern3.../
  3. 作为实例方法:pattern1.then(pattern2) => pattern # /pattern1patern2/

如何扩展常用模式

使用的提议方案.domain()似乎是一个常见的正则表达式。将用户定义的模式视为一种方法并不容易添加新模式。在像 Java 这样的语言中,库的用户必须重写该类才能为常用模式添加方法。

我建议将每件作品都视为一个对象来解决这个问题。可以为每个常用的正则表达式创建一个模式对象,例如匹配域。鉴于我之前关于捕获的想法,确保捕获适用于包含捕获部分的相同常见模式的多个副本并不难。

还应该有用于匹配各种字符类的模式的常量。

零宽度环顾断言

扩展我的想法,即所有部分都应该隐式聚集。使用实例方法环顾断言不应该太难。

pattern.zeroWidthLookBehind()会产生(?<patten).


仍然需要考虑的事情。

  • 反向引用:希望前面讨论的命名捕获不会太难
  • 如何实际实施。我没有过多考虑内部结构。这是真正的魔法发生的地方。
  • 翻译:确实应该有一个工具可以在经典正则表达式(比如 Perl 方言)和新方案之间进行转换。从新方案翻译可能是包的一部分

综上所述,我提议的与电子邮件地址匹配的模式版本:

Pattern domain_label = LETTER_CHARACTER + (LETTER_CHARACTER or "-" or DIGIT_CHARACTER).anyTimes()
Pattern domain = domain_label + ("." + domain_label).anyTimes()
Pattern pattern = (LETTER_CHARACTER + ALPHANUMERIC_CHARACTER + "@" + domain).compile

事后看来,我的方案大量借鉴了 Martin Fowler 的使用方法。虽然我不打算让事情这样发展,但它确实使使用这样的系统更易于维护。它还解决了 Fowler 方法(捕获顺序)的一两个问题。

于 2009-10-19T09:09:42.147 回答
7

您将如何设计 API?

我会从 Hibernate 标准 API 借用一个页面。而不是使用:

string("a").anyTimes().or().string("b").times(2,5).compile()

使用如下模式:

Pattern.or(Pattern.anyTimes("a"), Pattern.times("b", 2, 5)).compile()

这种表示法更简洁一些,我觉得更容易理解模式的层次/结构。每个方法都可以接受字符串或模式片段作为第一个参数。

你知道正则表达式的任何类似方法吗?

不是随便的,不。

你同意这种方法比使用简单的字符串更好吗?

是的,绝对......如果您将正则表达式用于任何远程复杂的事情。对于非常简短和简单的情况,字符串更方便。

你会在你的项目中使用这样一个简洁的实用程序吗?

可能,随着它被证明/稳定......将它滚动到像 Apache Commons 这样的更大的实用程序项目中可能是一个加分项。

你认为这会很有趣吗?;)

+1

于 2009-10-23T23:41:24.070 回答
7

我自己的卑微尝试可以在GitHub上找到。虽然我认为它不值得用于简单的表达式,但它确实提供了除了可读性改进之外的几个优点:

  • 它负责括号匹配
  • 它处理所有“特殊”字符的转义,这可能很快导致反斜杠地狱

几个简单的例子:

 // Matches a single digit
    RegExBuilder.build(anyDigit()); // "[0-9]"

 // Matches exactly 2 digits
    RegExBuilder.build(exactly(2).of(anyDigit())); // "[0-9]{2}"

 // Matches between 2 and 4 letters
    RegExBuilder.build(between(2,4).of(anyLetter())); // "[a-zA-Z]{2,4}"

还有一个更复杂的(或多或少验证电子邮件地址):

final Token ALPHA_NUM = anyOneOf(range('A','Z'), range('a','z'), range('0','9'));
final Token ALPHA_NUM_HYPEN_UNDERSCORE = anyOneOf(characters('_','-'), range('A','Z'), range('a','z'), range('0','9'));

String regexText = RegExBuilder.build(
 // Before the '@' symbol we can have letters, numbers, underscores and hyphens anywhere
    oneOrMore().of(
        ALPHA_NUM_HYPEN_UNDERSCORE
    ),
    zeroOrMore().of(
        text("."), // Periods are also allowed in the name, but not as the initial character
        oneOrMore().of(
            ALPHA_NUM_HYPEN_UNDERSCORE
        )
    ),
    text("@"),
 // Everything else is the domain name - only letters, numbers and periods here
    oneOrMore().of( 
        ALPHA_NUM
    ),
    zeroOrMore().of(
        text("."), // Periods must not be the first character in the domain
        oneOrMore().of(
            ALPHA_NUM
        )
    ),
    text("."), // At least one period is required
    atLeast(2).of( // Period must be followed by at least 2 letters (this is the TLD)
        anyLetter()
    )
);
于 2011-12-28T13:58:34.087 回答
6

.NET有一个流利的正则表达式库

于 2009-10-16T16:23:30.913 回答
4

简短的回答:我已经从 linting 和编译的角度看到了它,我认为这是需要考虑的。

长答案:我工作的公司为企业内容过滤应用程序制造基于硬件的正则表达式引擎。想想在网络路由器中以 20GB/秒的速度运行防病毒或防火墙应用程序,而不是占用宝贵的服务器或处理器周期。大多数反病毒、反垃圾邮件或防火墙应用程序的核心都是一堆正则表达式。

无论如何,正则表达式的编写方式对扫描的性能有很大的影响。你可以用几种不同的方式编写正则表达式来做同样的事情,其中​​一些会有更快的性能。我们为我们的客户编写了编译器和 linter 来帮助他们维护和调整他们的表达式。

回到 OP 的问题,而不是定义一个全新的语法,我会写一个 linter(对不起,我们的是专有的)剪切并粘贴正则表达式,这将打破传统的正则表达式并输出“流利的英语”,以便某人更好地理解。我还将添加相关性能检查和常见修改建议。

于 2009-10-20T19:01:28.633 回答
4

对我来说,简短的回答是,一旦你得到足够长的正则表达式(或其他做同样事情的模式匹配)导致问题......你可能应该考虑它们是否是正确的工具首先是为了这份工作。

老实说,任何流畅的界面似乎都比标准的正则表达式更难阅读。对于非常短的表达式,流利的版本是冗长的,但不会太长;它是可读的。但是这么长的东西的正则表达式也是如此。

对于中等规模的正则表达式,流畅的界面变得笨拙;足够长,即使不是不可能,也很难阅读。

对于一个很长的正则表达式(即电子邮件地址),正则表达式实际上很难(如果不是不可能的话)阅读,流畅的版本在 10 页前变得无法阅读。

于 2009-10-23T03:34:59.760 回答
1

你知道正则表达式的任何类似方法吗?

不,除了上一个答案

你同意这种方法比使用简单的字符串更好吗?

有点——我认为我们可以使用更具描述性的标记代替单个字符来表示构造,但我怀疑它会使复杂的逻辑更清晰。

您将如何设计 API?

将每个构造转换为方法名称,并允许嵌套函数调用,这样就很容易将字符串替换为方法名称。

我认为大部分价值将在于定义一个强大的实用函数库,例如匹配可以自定义的“电子邮件”、“电话号码”、“不包含 X 的行”等。

你会在你的项目中使用这样一个简洁的实用程序吗?

也许 - 但可能只适用于较长的,调试函数调用比调试字符串编辑更容易,或者有一个很好的实用程序函数可供使用。

你认为这会很有趣吗?;)

当然!

于 2009-10-16T16:43:25.953 回答
1

回答问题的最后一部分(荣誉)

private static final String pattern = "(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:"
    + "\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:("
    + "?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ "
    + "\\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\0"
    + "31]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\"
    + "](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+"
    + "(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:"
    + "(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)"
    + "?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\"
    + "r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?["
    + " \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)"
    + "?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t]"
    + ")*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?["
    + " \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*"
    + ")(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t]"
    + ")+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)"
    + "*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+"
    + "|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r"
    + "\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:"
    + "\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t"
    + "]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031"
    + "]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\]("
    + "?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?"
    + ":(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?"
    + ":\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?"
    + ":(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?"
    + "[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|"
    + "\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>"
    + "@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\""
    + "(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?"
    + ":[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\["
    + "\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\".\\[\\] \\000-"
    + "\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|("
    + "?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;"
    + ":\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[(["
    + "^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\""
    + ".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\"
    + "]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\"
    + "[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\"
    + "r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] "
    + "\\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]"
    + "|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\".\\[\\] \\0"
    + "00-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\"
    + ".|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,"
    + ";:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\"(?"
    + ":[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:["
    + "^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]"
    + "]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*("
    + "?:(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:("
    + "?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=["
    + "\\[\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t"
    + "])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t"
    + "])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?"
    + ":\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|"
    + "\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:"
    + "[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\"
    + "]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)"
    + "?[ \\t])*(?:@(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\""
    + "()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)"
    + "?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>"
    + "@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?["
    + " \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,"
    + ";:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t]"
    + ")*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\"
    + "\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?"
    + "(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\"."
    + "\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:"
    + "\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\["
    + "\"()<>@,;:\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])"
    + "*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])"
    + "+|\\Z|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\"
    + ".(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z"
    + "|(?=[\\[\"()<>@,;:\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:("
    + "?:\\r\\n)?[ \\t])*))*)?;\\s*)";

匹配符合 RFC 的电子邮件地址:D

于 2009-10-20T12:37:54.267 回答
1

正则表达式是对有限状态机的描述。经典的文本表示不一定是坏的。它紧凑,相对明确,并且被很好地采用。

更好的表示可能是状态转换图,但这可能很难在源代码中使用。

一种可能性是从各种容器和组合器对象构建它。

类似于以下内容(将其从伪代码转换为选择的语言留给渴望的人练习):

domainlabel = oneormore(characterclass("a-zA-Z0-9-"))
分隔符 = 文字(“。”)
域 = 序列(一个或多个(序列(域标签,分隔符)),域标签)
localpart = oneormore(characterclassnot("@"))
电子邮件地址 = 序列(本地部分,文字(“@”),域)

请注意,以上将错误地将任意许多电子邮件地址分类为有效但不符合他们需要遵循的语法,因为该语法需要的不仅仅是一个简单的 FSM 来进行完整解析。不过,我不相信它会将有效地址误分类为无效地址。

它应该对应于 [^@]+@([a-zA-Z0-9-]+.)+.([a-zA-Z0-9-]+)

于 2009-10-20T13:50:18.370 回答
1

4. 你会在你的项目中使用这样一个简洁的工具吗?

我很可能不会。我认为这是使用正确工具完成工作的一个案例。这里有一些很好的答案,例如: Jeremy Stein 的1579202。我最近在最近的一个项目中“获得”了正则表达式,发现它们非常有用,如果你知道语法,当正确评论时,它们是可以理解的。

我认为“了解语法”部分是让人们对正则表达式失去兴趣的原因,对于那些不理解正则表达式的人来说,它们看起来晦涩难懂,但它们是应用计算机科学中的强大工具(例如以编写软件为生)我觉得聪明的专业人士应该也应该能够学会使用它们并适当地使用它们。

正如他们所说的“权力越大,责任越大”。我见过人们在任何地方都使用正则表达式,但是那些花时间彻底学习语法的人明智地使用它们,它们非常有用;对我来说,再增加一层会在某种程度上破坏他们的目的,或者至少会夺走他们的力量。

这只是我自己的观点,我可以理解人们来自哪里,他们想要这样的框架,或者谁会避免使用正则表达式,但我很难从那些没有花时间的人那里听到“正则表达式不好”学习它们并做出明智的决定。

于 2009-10-23T16:31:06.903 回答
1

为了让每个人都开心(正则表达式大师和流体接口支持者),请确保流体接口可以输出适当的原始正则表达式模式,并使用工厂方法获取常规正则表达式并为其生成流体代码。

于 2009-10-23T23:48:47.157 回答
1

您可以在此处找到您要查找的内容: . 它是一个遵循向导设计模式的正则表达式构建器

于 2013-08-27T07:09:08.250 回答
1

我最近也有同样的想法

想自己实现它,但后来我发现了 VerbalExpressions

于 2014-06-30T21:58:18.377 回答
0

让我们比较一下:我经常使用 (N)Hibernate ICriteria 查询,这可以被认为是到 SQL 的Fluent映射。我曾经(并且仍然)对它们充满热情,但是它们是否使 SQL 查询更清晰?不,恰恰相反,但另一个好处是:以编程方式构建语句、对它们进行子类化和创建自己的抽象等变得更加容易。

我的意思是,如果做得好,为给定语言使用新界面是值得的,但不要太看重它。在许多情况下,它不会变得更容易阅读(嵌套减法字符类、后视捕获、if-branching 等一些难以流畅组合的高级概念)。但在许多情况下,更大的灵活性带来的好处超过了语法复杂性带来的额外开销。

要添加到您可能的替代方法列表中并将其排除在仅 Java 的上下文之外,请考虑 LINQ 语法。这是它的样子(有点做作)(fromwhere并且select是 LINQ 中的关键字):

// for ^str(aa|bb){3}
from part in mystring
where part startswith "str"
and part hasgroups "aa" or "bb" as first    /* "aa" or "bb" in group 'first' */
and part repeats first 3                    /* repeat group 'first' 3 times */
select part + "extra"                       /* can contain complete statement block */

只是一个粗略的想法,我知道。LINQ 的好处是它由编译器检查,这是一种语言中的一种语言。默认情况下,LINQ 也可以表示为流畅的链式语法,如果设计得当,它可以与其他 OO 语言兼容。

于 2009-10-23T02:39:27.517 回答
0

我说去吧,我相信实施起来很有趣。

我建议使用查询模型(类似于 jQuery、django ORM),其中每个函数都返回一个查询对象,因此您可以将它们链接在一起。

any("a").some("b").one("@").some(chars).one(".").some(chars) //a*b+@\w+\.\w+

wherechars被预定义为适合任何字符。

or可以通过使用选择来实现:

any("a").choice("x", "z") // a(x|z)

每个函数的参数可以是字符串或另一个查询。例如,chars上面提到的变量可以定义为查询:

//this one is ascii only
chars = raw("a-zA-Z0-9")

因此,如果使用流畅的查询系统感觉很麻烦,您可以拥有一个接受正则表达式字符串作为输入的“原始”函数。

实际上,这些函数可以只返回原始正则表达式,链接它们只是连接这些原始正则表达式字符串。

any("a") ---> "a*"
raw("b+") ----> "b+"
one(".") ---> "\."
choice("a", "b") ----> (a|b)
于 2009-10-24T00:19:00.383 回答
0

我不确定用流畅的 API 替换正则表达式会带来什么。

请注意,我不是正则表达式向导(几乎每次需要创建正则表达式时,我都必须重新阅读文档)。

一个流利的 API 将使任何中等复杂度的正则表达式(比如说约 50 个字符)甚至比所需的更复杂,最终也不容易阅读,尽管由于代码完成,它可能会改进 IDE 中正则表达式的创建。但是代码维护通常比代码开发成本更高。

事实上,我什至不确定是否有可能拥有一个足够智能的 API,以便在创建新的正则表达式时真正为开发人员提供足够的指导,而不是像前面的回答中提到的那样谈论模棱两可的情况。

您提到了 RFC 的正则表达式示例。我有 99% 的把握(仍有 1% 的希望;-))任何 API 都不会使该示例变得更简单,但相反只会使阅读变得更复杂!这是一个典型的例子,你无论如何都不想使用正则表达式!

即使关于正则表达式的创建,由于模棱两可的问题,很可能使用流畅的 API,您可能永远不会第一次得到正确的表达式,但必须多次更改,直到得到您真正想要的。

毫无疑问,我确实喜欢流畅的界面;我已经开发了一些使用它们的库,并且我使用了几个基于它们的第三方库(例如用于 Java 测试的 FEST)。但我不认为他们可以成为解决任何问题的金锤。

如果我们只考虑 Java,我认为正则表达式的主要问题是Java 字符串常量中反斜杠的必要转义。这使得在 Java 中创建和理解正则表达式变得异常困难。因此,对我来说,增强 Java 正则表达式的第一步是语言更改,如 Groovy,其中字符串常量不需要转义反斜杠。

到目前为止,这将是我在 Java 中改进正则表达式的唯一建议。

于 2009-10-25T01:34:01.820 回答
0

尝试使用RegexBee。它是一个流利的正则表达式生成器,可以作为一个不错的选择,因为它...

  • 流利
  • 不可变
  • 分层的、可组合的、可重用的
  • 基于功能接口
  • 高效的
  • 缓存的
  • 不需要静态导入只是为了紧凑
  • 开箱即用地支持最常用的正则表达式功能
  • 免费和开源

自述文件中的一个例子:

BeeFragment myFragment = Bee
        .then(Bee.BEGIN)
        .then(Bee.ASCII_WORD)
        .then(Bee.WHITESPACE.any())
        .then(Bee.ASCII_WORD.as("nameX"))
        .then(Bee.intBetween(-4, 1359)
                .or(Bee.fixed("-").more(Greediness.POSSESSIVE)))
        .then(Bee.ref("nameX"))
        .then(Bee.fixed("??)fixed?text. ").optional())
        .then(Bee.ASCII_WORD.optional(Greediness.LAZY))
        .then(Bee.END);

Pattern myPattern = myFragment.toPattern();
Matcher myMatcher = myPattern.matcher(someString);

if (myMatcher.matches()) {
    String nameX = myMatcher.group("nameX");
    System.out.println(String.format("We have a nice day, %s!", nameX));
}
于 2021-04-23T19:33:09.163 回答