我正在尝试使用正则表达式解析iCalendar(RFC2445)输入。
这是输入内容的[简化]示例:
BEGIN:VEVENT
abc:123
def:456
END:VEVENT
BEGIN:VEVENT
ghi:789
END:VEVENT
我想获得一系列匹配:“外部”匹配是每个VEVENT块,内部匹配是每个字段:值对。
我尝试过这个变种:
BEGIN:VEVENT\n((?<field>(?<name>\S+):\s*(?<value>\S+)\n)+?)END:VEVENT
但是考虑到上面的输入,结果似乎每个匹配的VEVENT只有一个字段,尽管+?捕获组:
**Match 1**
field def:456
name def
value 456
**Match 2**
field ghi:789
name ghi
value 789
在第一场比赛中,我预计会有两个领域:abc:123和def:456匹配......
我确定这是一个新手的错误(因为我似乎永远是一个新手,当谈到正则表达式......) - 但也许你可以指出我正确的方向?
谢谢!
答案 0 :(得分:2)
使用icalendar gem。 有关详细信息,请参阅Parsing iCalendars部分。
答案 1 :(得分:1)
您需要嵌套scan
。
string.scan(/^BEGIN:VEVENT\n(.*?)\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
答案 2 :(得分:1)
您需要将正则表达式拆分为一个匹配的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
答案 3 :(得分:0)
我认为问题是ruby MatchData
对象(regexp返回其结果)没有为多个具有相同名称的值提供任何规定。所以你的第二场比赛将覆盖第一场比赛。
答案 4 :(得分:0)
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(':')
分解每个结果字符串是微不足道的。
不要被试图做任何事情的正则表达式的警笛声所诱惑。它们在特定的地方非常强大和方便,但通常有更简单,更容易维护的解决方案。