Javascript 正则表达式从每个换行条目中捕获多行内容

Javascript Regex capture multiline content from each newline entry

我正在做 Javascript 正则表达式来处理一些原始数据并将其转换为二维数组。

任务简报(仅限 JS):

将原始字符串数据转换为二维数组。

原始数据输入:

这是一个包含 4 个条目的示例,一个新条目将换行。条目 3 带有多行内容。

2012/12/1, AM12:21 - user1‬: entry1_wasehhjdsaj

2012/12/2, AM9:42 - user2‬: entry2_bahbahbah_dsdeead

2012/12/2, AM9:44 - user3‬: entry3_Line1_ContdWithFollowingLine_bahbahbah

entry3_Line2_ContdWithABoveLine_bahbahbah_erererw

entry3_Line3_ContdWithABoveLine_bahbahbah_dsff

2012/12/4, AM11:48 - user7‬: entry4_bahbahbah_fggf

(原始字符串数据,没有空行。) 已更新:抱歉造成误导,内容的末尾不必使用相同的 END 模式,只需换行即可。

模式实际上是如何结束的?(感谢@Tim Pietzcker 的评论)。 内容应以换行符结尾,然后是下一个条目时间戳开始。 (您可以假设条目内容不包含任何类似的时间戳模式。)

我知道这可能是一个麻烦的正则表达式问题,因此任何其他实现相同目标的 JS 方法也将被接受。

我当前使用捕获组的正则表达式:

/^([0-9]{4}|[0-9]{2})[\/]([0]?[1-9]|[1][0-2])[\/]([0]?[1-9]|[1|2][0-9]|[3][0|1]), ([A|P])M([1-9]|1[0-2]):([0-5]\d) - (.*?): (.*)/gm

想要的捕获结果:

匹配 1

  1. 2012
  2. 12
  3. 1
  4. A
  5. 12
  6. 21
  7. user1‬
  8. entry1_wasehhjdsaj

匹配 2

  1. 2012
  2. 12
  3. 2
  4. A
  5. 9
  6. 42
  7. user2‬
  8. entry2_bahbahbah_dsdeead

第 3 场比赛

  1. 2012
  2. 12
  3. 2
  4. A
  5. 9
  6. 44
  7. user3‬
  8. entry3_Line1_ContdWithFollowingLine_bahbahbah entry3_Line2_ContdWithABoveLine_bahbahbah_erererw entry3_Line3_ContdWithABoveLine_bahbahbah_dsff

匹配 4

(被跳过...)


问题:

捕获条目3时出现问题,无法捕获条目3的第2行和第3行内容。如果条目仅包含一行内容,则正则表达式可以正常工作。

如何捕获包含多行内容的条目 3?我尝试使用 m 修饰符,但我不知道如何处理多行内容和换行符同时入场

如果用js正则表达式无法实现,请建议另一种js方法将原始数据转换为二维数组作为最终目标。

谢谢!

内容的结尾不必有相同的 END 模式,只需换行即可。

测试:https://regex101.com/r/eS9pY5/1

多行在 javascript 中无法正常工作,但您可以使用 [\s\S] 解决此问题。这个 class 匹配每个字符和 \n 。注意它后面的 *? 而不是 * ,以阻止它变得贪婪并且只去到第一个 END:

^([0-9]{4}|[0-9]{2})[\/]([0]?[1-9]|[1][0-2])[\/]([0]?[1-9]|[1|2][0-9]|[3][0|1]), ([A|P])M([1-9]|1[0-2]):([0-5]\d) - (.*?): ([\s\S]*?END)$

参见:https://regex101.com/r/mT8rI4/3

点 (.) 不匹配换行符。有一个字符 class 匹配所有内容 ([\S\s]),但您不想在没有预防措施的情况下使用它 - 否则 [\S\s]* 会立即匹配所有条目。

因此您需要告诉正则表达式引擎在下一次匹配开始时停止匹配。我们可以为此使用 negative lookahead assertion,我们只需将时间戳模式输入其中:

/^([0-9]{4}|[0-9]{2})\/(0?[1-9]|1[0-2])\/(0?[1-9]|[12][0-9]|3[01]), ([AP])M([1-9]|1[0-2]):([0-5]\d) - ([^:]*): ((?:(?!^([0-9]{4}|[0-9]{2})\/(0?[1-9]|1[0-2])\/(0?[1-9]|[12][0-9]|3[01]), ([AP])M([1-9]|1[0-2]):([0-5]\d))[\S\s])*)/gm

测试一下live on regex101.com

这是一个正则表达式,可以按照您需要的方式匹配您拥有的字符串:

^(\d{4}|\d{2})\/(0?[1-9]|1[0-2])\/(0?[1-9]|[12]\d|3[01]), ([AP])M([1-9]|1[0-2]):([0-5]\d) - (.*?): ((?:(?!(?:\d{4}|\d{2})\/(?:0?[1-9]|1[0-2])\/(?:0?[1-9]|[12]\d|3[01]))[\s\S])*)(?=\n|$)

demo

最后一个捕获组不再是匹配 .* 的贪婪点,而是匹配直到字符串末尾或日期模式的所有内容的调节贪婪标记 (?:(?!([0-9]{4}|[0-9]{2})\/(0?[1-9]|1[0-2])\/(0?[1-9]|[12][0-9]|3[01]))[\s\S])*

如果我们展开它以提高效率:

^(\d{4}|\d{2})\/(0?[1-9]|1[0-2])\/(0?[1-9]|[12]\d|3[01]), ([AP])M([1-9]|1[0-2]):([0-5]\d) - (.*?): (\D*(?:\d(?!(?:\d{3}|\d)\/(?:0?[1-9]|1[0-2])\/(0?[1-9]|[12]\d|3[01]))\D*)*)(?=\n|$)

another demo