代码之家  ›  专栏  ›  技术社区  ›  Zack

Regex将不在括号或大括号内的管道与嵌套块匹配

  •  0
  • Zack  · 技术社区  · 6 年前

    我正在尝试解析一些wiki标记。例如,以下内容:

    {{Some infobox royalty|testing
    | name = Louis
    | title = Prince Napoléon 
    | elevation_imperial_note= <ref name="usgs">{{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref>
    | a = [[AA|aa]] | b =  {{cite
    |title=TITLE
    |author=AUTHOR}}
    }}
    

    可以是要开始的文本。我先拆下起跑线 {{ 和结束 }} ,所以我可以假设这些都不见了。

    我想做什么 .split(<regex>) | 中的字符 [[AA|aa]] , <ref name="usgs">{{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref> ,和 {{cite|title=TITLE|author=AUTHOR}}

    [
     'testing'
     'name = Louis', 
     'title = Prince Napoléon', 
     'elevation_imperial_note= <ref name="usgs">{{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref>',
     'a = [[AA|aa]]',
     'b =  {{cite\n|title=TITLE\n|author=AUTHOR}}'
    ]
    

    在任何一点上都可能有断线,所以我不能只是寻找 \n| \s* \n*

    https://regex101.com/r/dEDcAS/2

    1 回复  |  直到 6 年前
        1
  •  1
  •   Cary Swoveland    6 年前

    下面是一个纯Ruby解决方案。我假设弦中的大括号和方括号是平衡的。

    str =<<BITTER_END
    Some infobox royalty|testing
    | name = Louis
    | title = Prince Napoléon 
    | elevation_imperial_note= <ref name="usgs">{{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref>
    | a = [[AA|aa]] | b =  {{cite
    |title=TITLE
    |author=AUTHOR}}
    BITTER_END
    

    stack = []
    last = 0
    str.each_char.with_index.with_object([]) do |(c,i),locs|
      puts "c=#{c}, i=#{i}, locs=#{locs}, stack=#{stack}" 
      case c
      when ']', '}'
        puts "  pop #{c} from stack"
        stack.pop
      when '[', '{'
        puts "  push #{c} onto stack"
        stack << c
      when '|'
        puts stack.empty? ? "  record location of #{c}" : "  skip | as stack is non-empty" 
        locs << i if stack.empty?
      end
        puts "  after: locs=#{locs}, stack=#{stack}" 
    end.map do |i|
      old_last = last
      last = i+1
      str[old_last..i-1].strip if i > 0
    end.tap { |a| a << str[last..-1].strip if last < str.size }
      #=> ["Some infobox royalty",
      #    "testing",
      #    "name = Louis", 
      #    "title = Prince Napoléon",
      #    "elevation_imperial_note= <ref name=\"usgs\">
      #      {{cite web|url={{Gnis3|1802764}}|title=USGS}}</ref>",
      #    "a = [[AA|aa]]",
      #    "b =  {{cite\n|title=TITLE\n|author=AUTHOR}}"]
    

    1. .

    解释

    有关如何确定要拆分的管道符号位置的说明,请运行上面的Heredoc以确定 str (首先需要取消Heredoc的缩进),然后运行以下代码。一切都将揭晓(输出很长,所以请关注对数组的更改 locs stack .)

    stack = []
    str.each_char.with_index.with_object([]) do |(c,i),locs|
      puts "c=#{c}, i=#{i}, locs=#{locs}, stack=#{stack}" 
      case c
      when ']', '}'
        puts "  pop #{c} from stack"
        stack.pop
      when '[', '{'
        puts "  push #{c} onto stack"
        stack << c
      when '|'
        puts stack.empty? ? "  record location of #{c}" : "  skip | as stack is non-empty" 
        locs << i if stack.empty?
      end
        puts "  after: locs=#{locs}, stack=#{stack}" 
    end
      #=> [20, 29, 44, 71, 167, 183]
    

    如果需要,可以确认支架和支架的平衡如下。

    def balanced?(str)
      h = { '}'=>'{', ']'=>'[' }
      stack = []
      str.each_char do |c|
        case c
        when '[', '{'
          stack << c
        when ']', '}'
          stack.last == h[c] ? (stack.pop) : (return false)
        end
      end   
      stack.empty?
    end
    
    balanced?(str)
      #=> true
    
    balanced?("[[{]}]")
      #=> false
    

    为了透明度,有机会使用某个词 .

        2
  •  0
  •   Marnen Laibow-Koser    5 年前

        3
  •  0
  •   Casimir et Hippolyte    5 年前

    使用split方法拆分字符串通常比扫描所需的子字符串更复杂。

    跳过括在括号中的管道相对容易,您所要做的就是定义能够匹配最终嵌套括号的子模式,并在主模式中使用它们。这样,它们之间的管道就被忽略了。

    确保与干管外的管道不匹配 {{...}} \G \克 是最后一场成功的比赛后该位置的锚。它确保每一场比赛都与上一场比赛保持连续。从闭幕式开始 }} 永远不会在主模式中使用,您可以确定,当达到此模式时,该模式将失败,并且不可能进行进一步的匹配。

    pat = /
      # subpatterns
      (?<cb>  { [^{}]*+   (?: \g<cb> [^{}]*   )*+  } ){0} # curly brackets
      (?<sb> \[ [^\]\[]*+ (?: \g<sb> [^\]\[]* )*+ \] ){0} # square brackets
    
      (?<nbpw> [^|{}\]\[\s]+ ){0} # no brackets, pipes nor white-spaces
    
      # main pattern
      (?:
          \G (?!\A) \s* # other contigous matches branch
        |
          {{ [^|{}]*+ # first match branch
          # check if curly brackets are balanced until }} (optional but recommended)
          (?= [^{}]*+ (?: \g<cb> [^{}]* )*+ }} )
      )
      \| \s* 
    
      (?<result>
          \g<nbpw>?
          (?: \s* (?: \g<cb> | \g<sb> | \s \g<nbpw> ) \g<nbpw>? )*
      )
    /x
    
    str.scan(pat).map{|item| item[3]}
    

    请注意,已经为空白修剪了结果。

    {{...}} 一次块,在模式的第二个分支周围添加一个捕获组,以知道下一个块何时开始。