【问题标题】:EBNF for capturing a comma between two optional valuesEBNF 用于捕获两个可选值之间的逗号
【发布时间】:2019-05-07 17:47:21
【问题描述】:

我有两个可选值,当两者都存在时,它们之间需要一个逗号。如果存在一个或两个值,则可能结尾有逗号,但如果不存在任何值,则不允许使用逗号。

有效例子:

(first,second,)
(first,second)
(first,)
(first)
(second,)
(second)
()

无效示例:

(first,first,)
(first,first)
(second,second,)
(second,second)
(second,first,)
(second,first)
(,first,second,)
(,first,second)
(,first,)
(,first)
(,second,)
(,second)
(,)
(,first,first,)
(,first,first)
(,second,second,)
(,second,second)
(,second,first,)
(,second,first)

我有足够的 EBNF 代码 (XML-flavored),但是有没有办法可以简化它?我想让它更具可读性/更少重复。

tuple ::= "(" ( ( "first" | "second" | "first" "," "second" ) ","? )? ")"

如果用正则表达式更容易理解,这里是等效代码,但我需要 EBNF 中的解决方案。

/\(((first|second|first\,second)\,?)?\)/

这是一个有用的铁路图:

当我们将其抽象为三个词时,这个问题变得更加复杂:"first""second""third" 都是可选的,但它们必须按顺序出现,用逗号,带有可选的尾随逗号。我能想到的最好的方法是蛮力方法:

"(" (("first" | "second" | "third" | "first" "," "second" | "first" "," "third" | "second" "," "third" | "first" "," "second" "," "third") ","?)? ")"

显然,涉及 O(2n) 复杂度的解决方案并不是很理想。

【问题讨论】:

  • 如果您只使用firstsecond ,那么需要什么正则表达式?
  • @WJS 我将我的问题减少到the simplest case。在我的真实代码中,"first""second" 实际上是复杂的表达式。出于 StackOverflow 的目的,它们是什么并不重要,所以我将它们设为原子。封装 FTW!

标签: regex big-o ebnf


【解决方案1】:

我找到了一种简化它的方法,但不是很多:

"(" ( ("first" ("," "second")? | "second") ","? )? ")"

对于三项解决方案,取二项解决方案并在第一项之前添加:

"(" (("first" ("," ("second" ("," "third")? | "third"))? | "second" ("," "third")? | "third") ","?)? ")"

对于任何 (n+1) 项解决方案,取 n 项解决方案并添加第一项。这个复杂度是O(n),明显优于O(2n)

【讨论】:

    【解决方案2】:

    这个表达式可能会帮助你设计一个更好的表达式。您可以只使用捕获组并从左向右滑动并传递您可能的输入,可能类似于:

    \((first|second|)(,|)(second|)([\)|,]+)
    

    我只是猜测您希望捕获中间逗号:

    这可能不是您想要的确切表达方式。但是,它可能会以一种简单的方式向您展示如何做到这一点:

    ^(?!\(,)\((first|)(,|)(second|)([\)|,]+)$
    

    你可以在你的表情左右添加更多的边界,可能类似于this expression

    此图显示了第二个表达式的工作原理:

    性能

    这个 JavaScript sn-p 显示了使用简单的 100 万次 for 循环的第二个表达式的性能,以及它如何使用 $1$3 捕获 firstsecond

    repeat = 1000000;
    start = Date.now();
    
    for (var i = repeat; i >= 0; i--) {
    	var string = "(first,second,)";
    	var regex = /^(?!\(,)\((first|second|)(,|)(second|)([\)|,]+)$/gms;
    	var match = string.replace(regex, "$1 and $3");
    }
    
    end = Date.now() - start;
    console.log("YAAAY! \"" + match + "\" is a match ??? ");
    console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test. ? ");

    【讨论】:

    • 我不确定这是否有效。它不会也捕获(second,second) 吗?我会在我的问题中添加一些无效示例供您测试。
    • 另外,您使用哪个工具来生成铁路图?看起来很有用。
    • 显然,您只使用firstsecond 这两个词,那么您为什么不只匹配合法的案例呢?
    • @Emma 呃。我问的是OP。我在错误的部分发表了评论。也许我不明白这个问题?
    【解决方案3】:

    我不熟悉 EBNF,但我熟悉 BNF 和解析器语法。以下只是基于我自己的正则表达式的变体。我假设未加引号的括号不被视为标记,而是用于对相关元素进行分组。

      tuple ::= ( "(" ( "first,second" | "first" | "second" ) ","? ")" ) | "()"
    
    • 匹配 (first,second(first(second
    • 然后匹配可选的,
    • 后跟一个结束括号。 )
    • 或空括号分组​​。 ()

    但我怀疑这是一种改进。

    这是我的 Java 测试代码。测试数据中的前两行字符串匹配。其他人没有。

          String[] testdata = {
                "(first,second,)", "(first,second)", "(first,)", "(first)",
                "(second,)", "(second)", "()",
    
                "(first,first,)", "(first,first)", "(second,second,)",
                "(second,second)", "(second,first,)", "(second,first)",
                "(,first,second,)", "(,first,second)", "(,first,)", "(,first)",
                "(,second,)", "(,second)", "(,)", "(,first,first,)",
                "(,first,first)", "(,second,second,)", "(,second,second)",
                "(,second,first,)", "(,second,first)"
          };
    
          String reg = "\\(((first,second)|first|second),?\\)|\\(\\)";
          Pattern p = Pattern.compile(reg);
    
          for (String t : testdata) {
             Matcher m = p.matcher(t);
             if (m.matches()) {
                System.out.println(t);
             }
          }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-10
      • 2020-01-15
      • 2019-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多