【问题标题】:Regex for checking if a string has mismatched parentheses?正则表达式检查字符串是否有不匹配的括号?
【发布时间】:2009-02-18 20:08:06
【问题描述】:

在 PHP 脚本中,我应该使用什么正则表达式来检查字符串中不匹配的括号?我想允许的事情包括:

  • 这是(好的)
  • 这(是)(好的)

我想防止的事情:

  • 这是)不好(
  • 这也是(不好的
  • 这(太糟糕了)

谢谢!

更新:你们都很摇滚。使用正则表达式执行此操作似乎比它应该具有的更棘手,而这些 2 级答案是使 stackoverflow 变得美丽的原因。感谢您的链接和伪代码。我不知道该给谁答案,所以我向所有我不能接受答案的人道歉。

【问题讨论】:

  • 您是否需要任意嵌套的括号,或者您确定在任何输入字符串中嵌套的嵌套级别不超过一个固定级别(例如 5 级深)?
  • 字符串限制为大约 300 个字符。当然有可能获得 300 (s in a row)。啊,用户输入 :)

标签: php regex


【解决方案1】:

Regex 不是适合这项工作的工具。手动扫描字符串。

伪代码:

depth = 0
for character in some_string:
    depth += character == '('
    depth -= character == ')'
    if depth < 0:
       break

if depth != 0:
   print "unmatched parentheses"

【讨论】:

    【解决方案2】:

    可以使用正则表达式来做到这一点——PHP 使用的 PCRE 允许递归模式。 PHP 手册提供了一个example,这几乎正是您想要的:

    \(((?>[^()]+)|(?R))*\)
    

    这匹配任何正确的括号子字符串,只要它以括号开头和结尾。如果你想确保整个字符串是平衡的,允许字符串像 “wiggedy(wiggedy)(wiggedy(wack))”,这是我想出的:

    ^((?:[^()]|\((?1)\))*+)$
    

    下面是对模式的解释,可能比混淆更具有启发性:

    ^ 字符串的开头 (启动“平衡子串”组(递归调用) (?: 启动“最小平衡子串”组 [^()] 最小平衡子串是非括号字符 |或者 \((?1)\) 一组包含平衡子串的括号 ) 完成“最小平衡子串”组 * 我们的平衡子串是最小的最大序列 平衡子串 + 一旦我们匹配了一个最大序列就不要回溯 ) 完成“平衡子串”模式 $ 字符串结尾

    这类正则表达式需要考虑很多效率和正确性。小心点。

    【讨论】:

    • +1。 Perl 也可以通过 (??{ code }) 扩展来做到这一点。许多(大多数?)现代语言的“正则表达式”引擎比正则语言/DFA 更强大。
    • Perl 具有相同的 (?R) 子组。但是这个特性,以及 (?? { code }),是高度实验性的,并且文档指出它们的确切副作用可能会在版本之间发生变化。所以对于生产代码,不要尝试在有限自动机 (PCRE) 中使用 hack。
    • 克里斯,你有关于“高度实验性”警告的参考吗? pcre.org/pcre.txt 的 PCRE 手册页没有这样说。
    • 这可能是一个非常危险的功能。如果您使用捕获组,使用递归标志可能会导致灾难性(PCRE 崩溃)回溯,以及过度(脚本终止)内存消耗。对于像这样的简单情况,最好不要使用正则表达式!
    • 一般来说,正则表达式都是如此。不使用递归很容易发生灾难性的回溯。正则表达式最棒的地方在于它们令人难以置信的强大功能和灵活性。正则表达式最糟糕的地方在于它们令人难以置信的强大功能和灵活性。
    【解决方案3】:

    使用正则表达式是不可能做到这一点的。大括号匹配需要正则表达式中不可用的递归/计数功能。为此,您需要一个解析器。

    更多详情请点击此处:http://blogs.msdn.com/jaredpar/archive/2008/10/15/regular-expression-limitations.aspx

    【讨论】:

    • 递归和计数是某些语言的正则表达式引擎中的可用功能,例如Perl 和 PHP 的。请参阅 Bennett McElwee 的回答。 Perl 在 CPAN 中有一个 Regexp::Common::balanced 模块来执行此操作。
    • @Brian,这更像是一个语义论点,但就正则表达式可以进行递归而言,它实际上不再是正则表达式了。这是一个上下文无关的语法。我意识到这对大多数人来说真的没有任何意义,并且递归正在迅速成为正则表达式的必备功能。
    • 你是对的,但我认为在 OP 的问题的上下文中,重要的是要指出,无论 PHP 调用什么“正则表达式”,它实际上都可以做他要求它做的事情。 (希望它们在一段时间内仍被称为“正则表达式”,cfgex 不会那么好。:)
    • @Brian,我实际上没有意识到 PHP 正则表达式具有递归功能(通常适用于 .Net 版本)。正则表达式很糟糕。我喜欢 modregex 但这会让 perl 的人感到困惑。也许是 rrregex(递归就绪正则表达式)。不过,你必须用一种虎皮糖霜的声音才能说出来。
    【解决方案4】:

    您的示例不包含任何嵌套括号……如果您不关心嵌套,则可以使用以下表达式来完成:

    ^[^()]*(?:\([^()]*\)[^()]*)*$
    

    这将与“允许”列表中的所有字符串匹配,而与“阻止”列表中的字符串匹配。但是,它也会对任何带有嵌套括号的字符串失败。例如“这(是(不)好)”

    正如其他人已经指出的那样,如果您需要处理嵌套,正则表达式不是正确的工具。

    【讨论】:

      【解决方案5】:

      同意使用正则表达式这是不可能的事实。不过,您可以执行以下操作:

      <?php
      
      $testStrings = array( 'This is (ok)', 'This (is) (ok)', 'This is )bad(', 'This is also (bad', 'This is (bad (too)' );
      
      foreach( $testStrings as $string ) {
          $passed = hasMatchedParentheses( $string ) ? 'passed' : 'did not pass';
          echo "The string $string $passed the check for matching parenthesis.\n";
      }
      
      function hasMatchedParentheses( $string ) {
          $counter = 0;
          $length = strlen( $string );
          for( $i = 0; $i < $length; $i ++ ) {
              $char = $string[ $i ];
              if( $char == '(' ) {
                  $counter ++;
              } elseif( $char == ')' ) {
                  $counter --;
              }
              if( $counter < 0 ) {
                  return false;
              }
          }
          return $counter == 0;
      }
      
      ?>
      

      【讨论】:

        【解决方案6】:

        为了扩展 JaredPar 的答案,不使用正则表达式并不难解决,只需编写一个函数来检查字符串中的每个字符并增加/减少计数器。如果你找到一个“(”,增加它,如果你找到一个“)”,减少它。如果计数器低于 0,则可以中断,字符串无效。处理完整个字符串后,如果计数器不为 0,则存在不匹配的左括号。

        【讨论】:

          【解决方案7】:

          为什么不能使用正则表达式

          其他答案都是正确的,但我只是想为理论计算机科学加上一个插件......这是一个知道理论会带来实际实际优势的案例。

          正则表达式对应于确定性有限自动机 (DFA),但括号匹配需要上下文无关文法,该文法可以实现为有限自动机 (PDA),但不能通过 DFA 实现。

          因此,无需大量额外的脑力劳动,我们就知道答案是否定的,而且我们不必担心我们忽略了某些东西。因此,您可以对上述答案充满信心,而不必担心作者在给出答案时只是忽略了某些内容。

          几乎所有编译器书籍都会谈到这一点,这里有一个快速概述:

          http://books.google.com/books?id=4LMtA2wOsPcC&pg=PA94&lpg=PA94&dq=push-down+finite+automata&source=bl&ots=NisYwNO1r0&sig=ajaSHFXwpPOWG8IfbcfKoqzS5Wk&hl=en&ei=m26cSdf6DZGYsAPB-6SsAg&sa=X&oi=book_result&resnum=6&ct=result

          【讨论】:

          • 这并不完全正确。在 PHP 和许多其他上下文中使用的正则表达式实际上并不是正式意义上的正则表达式。它们添加了很多功能,使它们更有用,并将它们扩展到真正的正则表达式之外。这就是问题可以解决的原因。
          • 嗯,什么功能?您有适用于“((()))”,“()()()”,“(((”或“()))”的示例吗?
          • 递归子模式特性。请参阅我对这个问题的回答,它正确处理了您给出的四个示例。
          【解决方案8】:

          没有正则表达式的工作 php:

          function analyse($input){
              $len = strlen($input);
              $depth = 0;
              for ($i = 0; $i < $len; $i++) {
                  $depth += $input[$i] == '(';
                  $depth -= $input[$i] == ')';
                  if ($depth < 0) break;
              }
              if ($depth != 0) return false;
                  else return true;
          }
          $check_nestled = analyse('(5 * 2) + ((2 + 2) - 4)');
          if($check_nestled){
              // do stuff, everything is ok
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-18
            • 2017-09-15
            • 2023-03-04
            • 1970-01-01
            • 1970-01-01
            • 2022-11-12
            相关资源
            最近更新 更多