从字符串中删除不匹配的括号

时间:2011-03-24 20:15:02

标签: ruby regex string recursion text-processing

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

即,除非字符串中的(后面跟着),否则应删除所有)。同样,应该删除字符串中某处(之前没有"(a)".remove_unmatched_parents # => "(a)" "a(".remove_unmatched_parents # => "a" ")a(".remove_unmatched_parents # => "a" 的所有{{1}}。

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

E.g:

{{1}}

5 个答案:

答案 0 :(得分:7)

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

(非常简单的)过程可能是:

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

  1. 如果它不是'('或')',那么只需将其附加到输出
  2. 即可
  3. 如果是'('增加一个seen_parens计数器并添加它
  4. 如果是')'并且 seen_parens是> 0,添加它并减少seen_parens。否则跳过它。
  5. 在过程结束时,如果seen_parens是> 0然后从最后开始删除那么多的parens。 (可以使用堆栈或递归将此步骤合并到上述过程中。)

    整个过程为O(n),即使开销相对较高

    快乐的编码。

答案 1 :(得分:3)

以下使用oniguruma。如果您使用ruby1.9,Oniguruma是内置的正则表达式引擎。如果您使用的是ruby1.8,请参阅:oniguruma

<强>更新

我一直懒得复制并粘贴别人的正则表达式。它似乎有问题。

所以现在,我写了自己的。我相信它现在应该有效。

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

更新2

这更简单。

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

答案 2 :(得分:2)

构建一个简单的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个月并试图记住他们在做什么!

答案 3 :(得分:1)

这是我的解决方案,基于@ 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

答案 4 :(得分:0)

算法:

  1. 遍历给定的字符串。
  2. 在执行此操作时,请跟踪“(”堆叠中的位置。
  3. 如果找到任何“)”,请从堆栈中删除顶部元素。
    • 如果stack为空,请从字符串中删除“)”。
  4. 最后,我们可以拥有无​​与伦比的大括号,如果有的话。
  5. Java代码: 出席@ http://a2ajp.blogspot.in/2014/10/remove-unmatched-parenthesis-from-given.html