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



我正在尝试解析一些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 =  {{citen|title=TITLEn|author=AUTHOR}}'
]

在任何一点都可能有换行符,所以我不能只寻找n|。如果里面有多余的空白,那也没关系。我可以很容易地去掉多余的s*n*

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

下面是一个纯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 =  {{citen|title=TITLEn|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。。。并且,为了透明起见,有机会使用某个词

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

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

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

为了确保不匹配主{{...}}块之外的管道(如果有的话(,必须使用基于G的模式。CCD_ 17是用于最后一次成功匹配之后的位置的锚。它确保每一场比赛都与前一场比赛连续。由于关闭的}}从未在主模式中被消耗,因此可以确信,当达到这个模式时,该模式将失败,并且不可能有进一步的匹配。

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]}

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

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

相关内容

  • 没有找到相关文章

最新更新