正则表达式仅匹配第一个或第二个匹配项

时间:2019-05-05 16:34:52

标签: javascript regex

我需要在字符串中找到两位数字,但是我只知道是否需要前两位数字或后两位数字,不知道它们在字符串中的确切位置以及包围它们的地方。字符串是日期,但出于所有意图和目的,格式都是随机的。
到目前为止,我想到了这一点:

(?<=\D)\d{1,2}(?=\D)

它符合我的需要,但是我需要在第一次出现或第二次出现之后停止匹配,因此在这些示例中几乎找不到所有内容:

2019-01-05 23:59:59
2019 01 05 23:59:59
2019. 01. 05. 23:59:59
2019.01.05. 23:59:59
05-01-2019 23:59:59
5-1-2019 23:59:59
05/01/2019 23:59:59
5/1/2019 23:59:59
5 1 2019 23:59:59
05 1 2019 23:59:59
05. 1. 2019 23:59:59
5. 1. 2019 23:59:59

基本上,我想在每行中匹配“ 1” /“ 01”或“ 5” /“ 05”。
我已经搜索了很多论坛,但找不到任何有帮助的解决方案,似乎该解决方案在任何地方都依赖于字符串,而并不完全是“仅查找第n个出现的情况”。就我而言,那确实是解决问题的唯一方法,至少我无法提出每次都肯定匹配的模式。上面的示例甚至不是唯一的可能性,格式化日期的任何方式都是其中之一,我唯一要知道的事实是它在我要搜索的所有文档中都是一致的,日期中总是带有分隔符,并且它早于时间。

2 个答案:

答案 0 :(得分:1)

This expression可能会帮助您仅获取所需月份和日期的首次出现:

[-\s.]+(\d{2})[-\s.]+(\d{2})[\s\S]*

这不是最好的表达方式,但是它可能会让您大致了解,贪婪的字符[\s\S]*会如何遍历其余不需要的字符。

您可以根据需要简单地更改我的初始边界,也可以对原始表达进行细微的更改,仅在[\s\S]*之后。

enter image description here

该图显示了它的工作方式:

enter image description here

答案 1 :(得分:0)

您可以匹配“喜欢日期”模式,并使用捕获组来提取月份或日期部分。月和日部分位于第一个或第三个捕获组中,并且要获得一致的定界符,可以对捕获组使用反向引用。

要获取单独的值,您可以在匹配数字\D时进行拆分

(?:\d{4}(([- .]|\. )\d{2}\2\d{2})\.? |(\d{1,2}([-\/ ]|\. )\d{1,2})\4\d{4} )\d{2}:\d{2}:\d{2}
  • (?:非捕获组
    • \d{4}匹配4位数字
    • (捕获组1
      • (捕获组2
        • [- .]|\.匹配-/,空格或点和空格
      • )关闭捕获组2
    • \d{2}\2\d{2}匹配2位数字,向后引用第2组,2位数字
    • )关闭第1组
  • \.?匹配可选的点和空格
  • |
  • (捕获组3
    • \d{1,2}匹配1-2位数字
    • ([-\/ ]|\.? )组4,匹配-/,空格或点和空格
  • \d{1,2})\4\d{4}匹配1-2位数字,向后引用第4组和第4位数字
  • )关闭非捕获组
  • \d{2}:\d{2}:\d{2}匹配“喜欢时间”部分

Regex demo

例如:

let pattern = /(?:\d{4}(([- .]|\. )\d{2}\2\d{2})\.? |(\d{1,2}([-\/ ]|\.? )\d{1,2})\4\d{4} )\d{2}:\d{2}:\d{2}/;
[
  "2019-01-05 23:59:59",
  "2019 01 05 23:59:59",
  "2019. 01. 05. 23:59:59",
  "2019.01.05. 23:59:59",
  "05-01-2019 23:59:59",
  "5-1-2019 23:59:59",
  "05/01/2019 23:59:59",
  "5/1/2019 23:59:59",
  "5 1 2019 23:59:59",
  "05 1 2019 23:59:59",
  "05. 1. 2019 23:59:59",
  "5. 1. 2019 23:59:59"
].forEach(s => {
  let m = s.match(pattern);
  let res = m[1] || m[3];
  console.log(res.split(/\D+/).filter(Boolean));
});

作为基于您的模式的替代方法,您可以使用2个负前行(?=来声明左边的内容,而不是右边的2个(?!,一个数字,但这不会考虑日期(如模式)。

要只获得一场比赛,您可以省略/g全局标志

/(?<!\d)\d{1,2}(?!\d)/

Regex demo

请注意,后面的外观不受广泛支持,并且可以在Chrome中使用。

[
  "2019-01-05 23:59:59",
  "2019 01 05 23:59:59",
  "2019. 01. 05. 23:59:59",
  "2019.01.05. 23:59:59",
  "05-01-2019 23:59:59",
  "5-1-2019 23:59:59",
  "05/01/2019 23:59:59",
  "5/1/2019 23:59:59",
  "5 1 2019 23:59:59",
  "05 1 2019 23:59:59",
  "05. 1. 2019 23:59:59",
  "5. 1. 2019 23:59:59"
].forEach(s => console.log(s.match(/(?<!\d)\d{1,2}(?!\d)/)[0]));

您可以使用捕获组并以字符串^或数字\D的开头来开始比赛,而不必担心:

(?:^|\D)(\d{1,2})(?!\d)

Regex demo