4

我正在尝试使用正则表达式解析 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 匹配......

我确定这是一个新手错误(因为在正则表达式方面我似乎永远是新手......) - 但也许你可以指出我正确的方向?

谢谢!

4

5 回答 5

2

使用icalendar宝石。有关更多信息,请参阅解析 iCalendars部分。

于 2012-10-26T22:37:29.487 回答
2

您需要将正则表达式拆分为一个匹配 aVEVENT和一个匹配名称/值对。然后,您可以使用嵌套scan来查找所有出现的情况,例如

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

于 2012-10-27T10:33:30.960 回答
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
于 2012-10-26T23:52:39.433 回答
0

我认为问题在于,MatchData正则表达式返回其结果的 ruby​​ 对象没有提供多个具有相同名称的值的任何规定。所以你的第二场比赛会覆盖第一场比赛。

于 2012-10-26T23:01:06.433 回答
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而且,从那里使用and分解每个结果字符串是微不足道的split(':')

不要被正则表达式的警报声所诱惑,试图做任何事情。它们在特定的地方非常强大和方便,但通常有更简单、更易于维护的解决方案。

于 2012-10-27T01:08:07.153 回答