如何在特定字符串前面找到所有出现的字符序列?

时间:2008-11-05 22:36:50

标签: ruby regex

我正在尝试从EBML定义中提取所有匹配项,如下所示:

| + A track
|  + Track number: 3
|  + Track UID: 724222477
|  + Track type: subtitles
...
|  + Language: eng
...
| + A track
|  + Track number: 4
|  + Track UID: 745646561
|  + Track type: subtitles
...
|  + Language: jpn
...

我希望所有出现的“语言:???”当前面有“Track type:subtitles”。我尝试了几种变体:

Track type: subtitles.*Language: (\w\w\w)

我在Ruby中使用多行修饰符,因此它匹配换行符(如其他语言中的's'修饰符)。

这样可以获得最后次出现,在上例中,它将是'jpn',例如:

string.scan(/Track type: subtitles.*Language: (\w\w\w)/m)
=> [["jpn"]]

我想要的结果:

=> [["eng"], ["jpn"]]

完成此任务的正确正则表达是什么?

2 个答案:

答案 0 :(得分:7)

你需要通过更改它来使你的正则表达式非贪婪:

.*

对此:

.*?

你的正则表达式从Track type: subtitles的第一次出现到Language: (\w\w\w)的最后一次出现是匹配的。使它非贪婪将起作用,因为它匹配尽可能少的字符。

答案 1 :(得分:3)

您需要使用延迟量词而不是.*。试试这个:

/Track type: subtitles.*?Language: (\w\w\w)/m

这可以让您在每个“Language: ???”之后第一次出现“Track type: subtitles:”。但如果某个跟踪(类型subtitles)缺少Language字段,则会感到困惑。


另一种方法是:

/^\| \+ (?:(?!^\| \+).)*?\+  Track type: subtitles$(?:(?!^\| \+).)*?^\|  \+ Language: (\w+)$/m

看起来有点凌乱,但应该照顾前一个问题。


更简洁的方法是对字符串进行标记:

/^\| \+ ([^\r\n]+)|^\|  \+ Track type: (subtitles)|^\|  \+ Language: (\w+)/m

(记下空格数)

对于每个匹配,您可以检查定义了哪个捕获组。任何一场比赛只有一组具有任何价值。

  • 如果是第一个组,则会启动新曲目。丢弃有关上一曲目的任何存储信息。
  • 如果是第二个组,则当前曲目的类型为subtitles
  • 如果是第三个组,则会找到此曲目的语言。
  • 每当您知道某个曲目的语言,并且该曲目属于subtitles时,请报告该曲目。