如何分解“{tags}”周围的字符串?

时间:2013-05-30 21:55:50

标签: ruby recursion template-engine

我正在编写一个可以有两种潜在输入形式的函数:

  1. This is {a {string}}
  2. This {is} a {string}
  3. 我调用用大括号“tags”包装的子字符串。我可能在字符串中有任意数量的标签,它们可以任意嵌套。

    我已经尝试编写一个正则表达式来抓取标签,这当然在嵌套标签上失败,抓住{a {string},缺少第二个花括号。我可以把它看作一个递归问题,但是在盯着错误的答案太久之后,我觉得我看到的东西看起来很明显。

    我可以做些什么来将潜在的标签分成几部分,以便处理和替换它们?

    更复杂的版本

    def parseTags( oBody, szText )
    
    
      if szText.match(/\{(.*)\}/)
        szText.scan(/\{(.*)\}/) do |outers|
          outers.each do |blah|
            if blah.match(/(.*)\}(.*)\{(.*)/)
              blah.scan(/(.*)\}(.*)\{(.*)/) do |inners|
                inners.each do |tags|
                  szText = szText.sub("\{#{tags}\}", parseTags( oBody, tags ))
                end
              end
            else
              szText = szText.sub("\{#{blah}\}", parseTags( oBody, blah ))
            end
          end
        end
      end
      if szText.match(/(\w+)\.(\w+)(?:\.([A-Za-z0-9.\[\]": ]*))/)
        func = $1+"_"+$2
        begin
          szSub = self.send func, oBody, $3
        rescue Exception=>e
          szSub = "{Error: Function #{$1}_#{$2} not found}"
          $stdout.puts "DynamicIO Error Encountered: #{e}"
        end
        szText = szText.sub("#{$1}.#{$2}#{$3!=nil ? "."+$3 : ""}", szSub)
      end
      return szText
    end
    

    这是修修太久的结果。它不干净,但它确实适用于类似于“1”的案例 - {help.divider.red.sys.["{pc.login}"]}被替换为---------------[ Duwnel ]---------------。然而,{pc.attr.str.dotmode} {ansi.col.red}|{ansi.col.reset} {pc.attr.pre.dotmode} {ansi.col.red}|{ansi.col.reset} {pc.attr.int.dotmode}闪烁着辉煌,随机的红色条纹和缺失的文字样片。

    为了解释,标记为{ansi.col.red}的任何内容都标记为ansi red代码,重置会转义颜色块,{pc.attr.XXX.dotmode}会在“o”中显示1到10之间的数字。

2 个答案:

答案 0 :(得分:2)

正如其他人所说,这是解析引擎的完美案例。正则表达式不倾向于很好地处理嵌套对。

Treetop是一个很棒的PEG解析器,您可能有兴趣看看。主要思想是在规则中定义要解析的所有内容(包括空格)。规则允许您递归地解析括号对之类的东西。

这是一个用于从嵌套括号对创建字符串数组的示例语法。通常语法是在一个单独的文件中定义的,但为了简单起见,我在最后包含了语法并用Ruby的DATA常量加载了它。

require 'treetop'

Treetop.load_from_string DATA.read

parser = BracketParser.new

p parser.parse('This is {a {string}}').value

#=> ["This is ", ["a ", ["string"]]]

p parser.parse('This {is} a {string}').value

#=> ["This ", ["is"], " a ", ["string"]]

__END__
grammar Bracket
   rule string
      (brackets / not_brackets)+
      {
         def value
            elements.map{|e| e.value }
         end
      }
   end

   rule brackets
      '{' string '}'
      {
         def value
            elements[1].value
         end
      }
   end

   rule not_brackets
      [^{}]+
      {
         def value
            text_value
         end
      }
   end
end

答案 1 :(得分:1)

我建议你不要在这个问题中使用更复杂的正则表达式,而是要研究一下Ruby的基于语法的解析引擎。可以在大多数这些语法中设计递归和嵌套语法。

parslet可能是您解决问题的好地方。类似erb的示例虽然没有展示嵌套,但可能最符合您的需求:https://github.com/kschiess/parslet/blob/master/example/erb.rb