【问题标题】:Remove unmatched parentheses from a string从字符串中删除不匹配的括号
【发布时间】:2011-07-22 10:48:19
【问题描述】:

我想从字符串中删除“非合作”括号。

即,所有( 都应该被删除,除非它们后面跟着一个) 在字符串中的某处。同样,所有) 前面没有( 在字符串中的某处都应该被删除。

理想情况下,算法也会考虑嵌套。

例如:

"(a)".remove_unmatched_parents # => "(a)"
"a(".remove_unmatched_parents # => "a"
")a(".remove_unmatched_parents # => "a"

【问题讨论】:

    标签: ruby regex string recursion text-processing


    【解决方案1】:

    也许可以考虑下推自动机,而不是正则表达式。 (我不确定 Ruby 正则表达式是否可以处理这个问题,我相信 Perl 可以)。

    一个(非常琐碎的)过程可能是:

    对于输入字符串中的每个字符:

    1. 如果不是 '(' 或 ')' 则只需将其附加到输出中
    2. 如果是 '(' 则增加 seen_parens 计数器并添加它
    3. 如果它是')' 并且 seen_parens > 0,添加它并减少seen_parens。否则跳过它。

    在过程结束时,如果 seen_parens > 0,则从末尾开始删除那么多括号。 (此步骤可以通过堆栈或递归的方式合并到上述过程中。)

    整个过程是O(n),即使开销比较高

    编码愉快。

    【讨论】:

    • 确实如此。这是字面上在世界范围内用于教学的示例,该示例无法使用正则表达式进行解析。现在,Ruby 的Regexp 比正则表达式强大得多,它们实际上可以解析这种语言,但它并不完全可维护。您可以在更短的时间内编写一个简单的递归下降解析器或下推自动机,甚至比您读取别人交给您的Regexp 所花费的时间还短,更不用说编写您自己的了。如果您将Regexp 拆分为多行以放入 cmets,那么自动机甚至会更短。
    • 感谢算法。我的回答 (stackoverflow.com/questions/5424959/…) 对你来说合适吗?
    【解决方案2】:

    以下使用oniguruma。如果您使用的是 ruby​​1.9,Oniguruma 是内置的正则表达式引擎。如果您使用的是 ruby​​1.8,请参阅:oniguruma

    更新

    我一直懒得只是复制和粘贴别人的正则表达式。好像有问题。

    所以现在,我自己写了。我相信它现在应该可以工作了。

    class String
        NonParenChar = /[^\(\)]/
        def remove_unmatched_parens
            self[/
                (?:
                    (?<balanced>
                        \(
                            (?:\g<balanced>|#{NonParenChar})*
                        \)
                    )
                    |#{NonParenChar}
                )+
            /x]
        end
    end
    
    • (?&lt;name&gt;regex1) 将(子)正则表达式 regex1 命名为 name,并使其可以被调用。
    • ?g&lt;name&gt; 将是一个代表regex1 的子正则表达式。注意这里?g&lt;name&gt; 不代表匹配regex1 的特定字符串,而是代表regex1 本身。实际上,可以将?g&lt;name&gt; 嵌入到(?&lt;name&gt;...) 中。

    更新 2

    这更简单。

    class String
        def remove_unmatched_parens
            self[/
                (?<valid>
                    \(\g<valid>*\)
                    |[^()]
                )+
            /x]
        end
    end
    

    【讨论】:

    • @pst 上一个居然有问题。所以现在,我自己写了一个不同的正则表达式。这一次,应该没问题。希望不要爆炸。
    【解决方案3】:

    这是我的解决方案,基于@pst 的算法:

    class String
      def remove_unmatched_parens
        scanner = StringScanner.new(dup)
        output = ''
        paren_depth = 0
    
        while char = scanner.get_byte
          if char == "("
            paren_depth += 1
            output << char
          elsif char == ")"
            output << char and paren_depth -= 1 if paren_depth > 0
          else
            output << char
          end
        end
    
        paren_depth.times{ output.reverse!.sub!('(', '').reverse! }
        output
      end
    end
    

    【讨论】:

    • 那么,remove_unmatched_parents 是错字吗?我是这么想的。
    【解决方案4】:

    构建一个简单的 LR 解析器:

    tokenize, token, stack = false, "", []
    
    ")(a))(()(asdf)(".each_char do |c|
      case c
      when '('
        tokenize = true
        token = c
      when ')'
        if tokenize
          token << c 
          stack << token
        end
        tokenize = false
      when /\w/
        token << c if tokenize
      end
    end
    
    result = stack.join
    
    puts result
    

    运行产量:

    wesbailey@feynman:~/code_katas> ruby test.rb
    (a)()(asdf)
    

    我不同意那些修改 String 类的人,因为你永远不应该打开一个标准类。正则表达式对于解析器来说非常脆弱并且难以支持。我无法想象 6 个月后回到以前的解决方案并试图记住他们在做什么!

    【讨论】:

      【解决方案5】:

      算法:

      1. 遍历给定的字符串。
      2. 在执行此操作时,跟踪堆栈中的“(”位置。
      3. 如果找到任何“)”,则从堆栈中删除顶部元素。
        • 如果堆栈为空,则从字符串中删除“)”。
      4. 最后,我们可以有不匹配的大括号的位置,如果有的话。

      Java 代码: 出席@http://a2ajp.blogspot.in/2014/10/remove-unmatched-parenthesis-from-given.html

      【讨论】:

        猜你喜欢
        • 2012-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-16
        相关资源
        最近更新 更多