0

我正在尝试解析一些 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

4

3 回答 3

1

以下是纯 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 需要先取消缩进),然后运行以下代码。一切都会揭晓。(输出很长,所以要关注数组的变化locsstack.)

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

1 ...并且,为了透明起见,有机会使用某个词

于 2018-10-31T21:06:08.447 回答
0

正则表达式不能处理任意嵌套(比如这里的括号),因此是这个解析问题的错误工具。如果您找不到现成的 MediaWiki 标记解析器,您将需要使用实际的解析器库(例如 Treetop),而不是正则表达式。

于 2019-10-09T17:26:07.027 回答
0

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

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

为确保不匹配主{{...}}块之外的管道(如果有),您必须使用\G基于模式。\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]}

请注意,结果已针对空格进行了修剪。

如果您想使用它{{...}}一次处理多个块,请在模式的第二个分支周围添加一个捕获组,以了解下一个块何时开始。

于 2019-10-15T16:46:25.790 回答