正则表达式匹配 2 个重复值之间的所有内容

Regex Matching everything between 2 repeating values

我正在做一个提醒应用程序,我希望能够支持 iCalender 导入。这就是为什么我希望能够提取事件。这是 iCalender 中事件的基本格式:

BEGIN:VEVENT
......
......
END:VEVENT

所有这些事件都在一个文件中,所以我将有一个像这样的大列表:

BEGIN:VEVENT
......
......
END:VEVENT
BEGIN:VEVENT
......
......
END:VEVENT

这些活动将有开始日期和结束日期

BEGIN:VEVENT
......
DTSTART;VALUE=DATE:20160402
DTEND;VALUE=DATE:20160403
......
END:VEVENT

当试图只提取事件时,它并不总是相同的格式。开始日期和结束日期可以在其他某些字段之前或之后。

目前我有:

/BEGIN:VEVENT[\s\S]*?DTSTART;VALUE=DATE:20160402[\s\S]*?END:VEVENT/

然而,这不仅仅匹配事件本身,它从 BEGIN:VEVENT 的第一场匹配开始匹配,匹配日期之前的所有内容,然后在接下来的 END:VEVENT 结束匹配。 =19=]

因此,在一些事件中,试图匹配它们的列表还包括许多其他事件。有没有一种方法可以匹配 DTSTART;VALUE=DATE: 并且只匹配前一个最近的 BEGIN:VEVENT 和后面的 END:VEVENT 只是为了提取当天的单个事件?

这个问题可以用一个经过调节的贪婪标记来解决,它可以用来获得文本中两个子字符串之间可能的最小 window。由于您的文本是多行的,因此您不能使用 . 原子来匹配任何字符,您需要使用 [^][\s\S].

所以,使用

/BEGIN:VEVENT((?:(?!\b(?:END|BEGIN):VEVENT\b)[\s\S])*DTSTART;VALUE=DATE:20160402[\s\S]*?)END:VEVENT/g

the regex demo

(?:(?!\b(?:END|BEGIN):VEVENT\b)[\s\S])* 部分匹配任何非 BEGIN:VEVENTEND:VEVENT 的文本(由于 \b 词边界,作为整个词)。

var re = /BEGIN:VEVENT((?:(?!\b(?:END|BEGIN):VEVENT\b)[\s\S])*DTSTART;VALUE=DATE:20160402[\s\S]*?)END:VEVENT/g; 
var str = 'BEGIN:VEVENT\n......\n......\nEND:VEVENT\nBEGIN:VEVENT\n......\n......\nEND:VEVENT\nThese events will have a start date and an end date\n\nBEGIN:VEVENT\n......\nDTSTART;VALUE=DATE:20160402\nDTEND;VALUE=DATE:20160403\n......\nEND:VEVENT';
var res = [];
 
while ((m = re.exec(str)) !== null) {
    res.push(m[0]);
}

document.body.innerHTML = "<pre>" + JSON.stringify(res.map(x => x.replace(/\r?\n/g, "<br/>")), 0, 4) + "</pre>";

注意 [\s\S]*? 也可以用上面的 tempered greedy token 代替,但似乎没有必要,因为 VEVENT 块是良构的并且没有嵌套的 VEVENT 块。如果有嵌套的 VEVENT 块,[\s\S]*? 应该替换为 tempered greedy token。