我正在尝试使用正则分析iCalendar(RFC2445)输入。
这是输入的样子的[简化]示例:
BEGIN:VEVENT
abc:123
def:456
END:VEVENT
BEGIN:VEVENT
ghi:789
END:VEVENT
我想获得一系列匹配:"外部"匹配是每个障碍块,内部匹配是每个字段:值对。
我尝试过的变体:
BEGIN:VEVENTn((?<field>(?<name>S+):s*(?<value>S+)n)+?)END:VEVENT
但是给出了上面的输入,尽管 ?在捕获组上:
**Match 1**
field def:456
name def
value 456
**Match 2**
field ghi:789
name ghi
value 789
在第一场比赛中,我本来可以期待两个领域:ABC:123和DEF:456匹配...
我敢肯定这是一个新手错误(因为我似乎永远是新手,而Regex的...) - 但也许您可以指向正确的方向?
谢谢!
您需要将正则拨号拆分为一个匹配的 VEVENT
,一个与名称/值对匹配的。然后,您可以使用嵌套的scan
查找所有发生,例如。G。
str.scan(/BEGIN:VEVENT((?<vevent>.+?))END:VEVENT/m) do
$~[:vevent].scan(/(?<field>(?<name>S+?):s*(?<value>S+?))/) do
p $~[:field], $~[:name], $~[:value]
end
end
其中 str
是您的输入。这输出:
"abc:1"
"abc"
"1"
"def:4"
"def"
"4"
"ghi:7"
"ghi"
"7"
如果要使代码更可读,我建议您使用require 'english'
,然后用$LAST_MATCH_INFO
$~
使用iCalendar Gem。有关更多信息,请参见"解析ICICARENDARS"部分。
您需要一个嵌套的scan
。
string.scan(/^BEGIN:VEVENTn(.*?)nEND:VEVENT$/m).each.with_index do |item, i|
puts
puts "**Match #{i+1}**"
item.first.scan(/^(.*?):(.*)$/) do |k, v|
puts "field".ljust(7)+"#{k}:#{v}"
puts "name".ljust(7)+"#{k}"
puts "value".ljust(7)+"#{v}"
end
end
将给出:
**Match 1**
field abc:123
name abc
value 123
field def:456
name def
value 456
**Match 2**
field ghi:789
name ghi
value 789
我认为问题是Ruby MatchData
对象(Regexp返回其结果)没有任何一个以上值的规定,具有相同名称。因此,您的第二场比赛覆盖了第一个。
ruby很少使用称为 slice_before
的使用方法,它非常适合这一点:
'BEGIN:VEVENT
abc:123
def:456
END:VEVENT
BEGIN:VEVENT
ghi:789
END:VEVENT'.split("n").slice_before(/^BEGIN:VEVENT/).to_a
导致:
[["BEGIN:VEVENT", "abc:123", "def:456", "END:VEVENT"],
["BEGIN:VEVENT", "ghi:789", "END:VEVENT"]]
从那里抓取内部数组元素很容易:
'BEGIN:VEVENT
abc:123
def:456
END:VEVENT
BEGIN:VEVENT
ghi:789
END:VEVENT'.split("n").slice_before(/^BEGIN:VEVENT/).map{ |a| a[1 .. -2] }
是:
[["abc:123", "def:456"], ["ghi:789"]]
,从那里,使用map
和split(':')
分解每个结果字符串是微不足道的。
不要被正式表达式的警笛声所吸引,试图做所有事情。它们在特定的位置非常强大和方便,但通常会更简单,更容易维护解决方案。