0

一般来说,我认为我没有遇到过这个问题的通用解决方案。如何匹配可以是范围或单个值的字符串?

假设我想匹配 [复杂] 日期:

  • 1999 - 2010
  • 公元前 323 年 - 公元 100 年
  • 公元前323年
  • 1995-99
  • 公元前 323 - 322 年

可以解析这两种情况的通用正则表达式“模板”是什么:

  1. 开始/结束日期(如果存在)
  2. 否则,只有一个日期

要匹配“1999 - 2010”,您可以这样做

/(\d+\s*)-(\s*\d+)/ // where $1 and $2 are start and end

要匹配更复杂的“323 BCE - 100 CE”,您可以这样做

/(\w+\s*\w+)\s*-\s*(\w+\s*\w+)/

为了匹配更简单的“323 BC”,你可以做

/\w+\s*\w+/

但是如何编写一个表达式首先检查范围(公元前 323 年 - 公元 100 年),如果不存在,检查单个值(公元前 323 年),它也可以处理上面列表中的其他示例?

4

3 回答 3

2

通过使匹配的后半部分成为可选。

/(\w+\s*\w+)(?:\s*-\s*(\w+\s*\w+))?/

示例(JavaScript)

"1900 - 2000".match(/(\w+\s*\w+)(?:\s*-\s*(\w+\s*\w+))?/);
//["1900 - 2000", "1900", "2000"]

"1900 BC".match(/(\w+\s*\w+)(?:\s*-\s*(\w+\s*\w+))?/);
//["1900 BC", "1900 BC", undefined]

请注意,外部的可选部分是不匹配的,因此结果数组仅包含您感兴趣的子匹配项。

在效率方面加强模式也是一个想法,例如寻找数字而不是任何字母数字,并且只允许单个空格(如果这是可以接受的)而不是零或更多。

于 2012-06-23T09:43:43.997 回答
0

您可能正在寻找这样的东西:

var pattern = /(\d+)(\s*(\w+))?(\s*-\s*(\d+)(\s*(\w+))?)?/;
var strings = [
    '1999 - 2010',
    '323 BCE - 100 CE',
    '323 BC',
    '1995-99',
    '323 - 322 BC'
];

for (var i=0, s; s = strings[i]; i++) {
    var m  = s.match(pattern);
    console.log(
        m[1], // beginning year
        m[3], // beginning b/c/e
        m[5], // end year
        m[7]  // end b/c/e
    );
}

哪个输出

1999  undefined  2010       undefined
323   BCE        100        CE
323   BC         undefined  undefined
1995  undefined  99         undefined
323   undefined  322        BC

这里的诀窍是理解这(group)?使得(group)可选。与此类似,(foo)+(foo){3}用于使组匹配至少一次或恰好三次。

(foo)默认情况下,组是捕获组。这意味着它们的结果将包含在 String#match() 返回的数组中。您可以像这样将组标记为非捕获:(?:wont-be-captured). 有了这个,我们可以进一步修改上述模式:

var pattern = /(\d+)(?:\s*(\w+))?(?:\s*-\s*(\d+)(?:\s*(\w+))?)?/;
for (var i=0, s; s = strings[i]; i++) {
    var m  = s.match(pattern);
    console.log(m[1], m[2], m[3], m[4]);
}
于 2012-06-23T10:39:12.120 回答
0

只是加入另一种可能按照您想要的方式工作的模式;

((\d+)( [A-Za-z]+|))((-| - )\d+( [A-Za-z]+|)|)

与 Utkanos 的模式一样,这可能需要一些收紧才能与其他任何东西不匹配。

于 2012-06-23T09:48:34.477 回答