我正在尝试解析一些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*
。
答案 0 :(得分: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),然后运行以下代码。一切都会揭晓。 (输出很长,因此请注意对数组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
1 ...为了透明起见,有机会使用某个单词。
答案 1 :(得分:0)
正则表达式不能处理任意嵌套(例如此处的括号),因此是解决此解析问题的错误工具。如果找不到现成的MediaWiki标记解析器,则需要使用实际的解析器库(例如Treetop),而不是正则表达式。
答案 2 :(得分:0)
使用分割方法分割字符串通常比扫描所需的子字符串更为复杂。
跳过包围在括号之间的管道是相对容易的,您要做的就是定义能够匹配最终嵌套括号的子模式,并在主模式中使用它们。这样,封闭在它们之间的管道将被忽略。
要确保与主{{...}}
块之外的管道不匹配(如果有的话),您必须使用基于\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]}
请注意,结果已针对空白进行了修剪。
如果要使用它一次处理多个{{...}}
块,请在模式的第二个分支周围添加捕获组,以了解下一个块何时开始。