2017-09-14 251 views
1

我试图从字符串中提取数字组。 这些数字既可以单独使用,也可以作为\d+ - \d+格式的范围,而两个数字之间的范围指示符可以不同,并且数字可以使用前缀M-STR。这些组可以在给定的字符串中出现1到n次,但是如果一个组后面跟着任何不是数字,空格或上面提到的前缀之一的字符,则匹配应该停止,即使后面还可以找到更多的数字。全局正则表达式匹配停止中间字符串

作为一个例子,下面几行

01 
05,07 
05, 7 
M-01, M-12 
311,STR 02 
M-56 
STR 17 
01 - Random String 25-31 Random other string 
M-04 Random String 01 
M-17,3,148,14 to 31 
M-17,3,STR 148,14 to 31 - Random String 
M-17,3,148,14- 31 Random, String 02 Random, other string 
STR 17,3,12 to 18, 148 ,M-14- 31 : Random String 02 

应该返回

01 
05;07 
05;7 
01;12 
311;02 
56 
17 
01 
04 
17;3;148;14 to 31 
17;3;148;14 to 31 
17;3;148;14- 31 
17;3;12 to 18;148;14- 31 

我使用javascript和运行

var pattern = /(\d+)\s?(?:-|~|to)?\s?(\d+)?/ig 
while (result = pattern.exec(line)) {console.log(result)} 

,但我几乎可以得到正确的结果不知道如何在第一个字符串后不匹配数字,即M-17,3,148,14 to 31 - Random string 46 Random string将retu值17;3;148;14 to 31;46,而46不应该匹配。

我并不是真的担心结果的格式,因为我无论如何正在消毒它们,因此'03 '返回为'03''03 '并不重要。对于数字范围也是如此,15 - 17既可以作为15 - 17返回,也可以如上例所示,使用捕获组来确定上限和下限,但我仍然需要能够判断两个数字是分开还是范围,所以5,8,10-12不能作为5;8;10;12返回。

我的最终目标是提取每行中的所有可能的值。在提取所有数字范围后,我循环遍历每个结果以获得所有可能的值,例如, 5,8,10-12将变成5; 8; 10; 11; 12。

如果它在某种程度上是可能的,而且这纯粹是可选的,我还想在最后一个数字范围后保留字符串,例如, STR 14, 23 Some String 18 Some other string应返回14;23并单独返回Some String 18 Some other string

如果有人有关于如何解决这个问题的想法,我将不胜感激。

回答

0

这是我的尝试。

[ 
 
    '01', 
 
    '05,07', 
 
    '05, 7', 
 
    'M-01, M-12', 
 
    '311,STR 02', 
 
    'M-56', 
 
    'STR 17', 
 
    '01 - Random String 25-31 Random other string', 
 
    'M-04 Random String 01', 
 
    'M-17,3,148,14 to 31', 
 
    'M-17,3,STR 148,14 to 31 - Random String', 
 
    'M-17,3,148,14- 31 Random, String 02 Random, other string', 
 
    'STR 17,3,12 to 18, 148 ,M-14- 31 : Random String 02', 
 
    '14 ~ 16', 
 
    'Random String 15', 
 
    '1to3', 
 
    'M-01 to STR 6', 
 
    '17 56' 
 
].forEach(function(str) { 
 
    var rangeRe = /(?:\s*,\s*)(?:M-|STR)?(\d+)(?:\s*(?:-|~|to)\s*(\d+))?/g, 
 
     ranges = [], 
 
     lastIndex = 1, 
 
     match; 
 

 
    str = ',' + str; 
 

 
    while (match = rangeRe.exec(str)) { 
 
     // Push a lower and upper bound onto the list of ranges 
 
     ranges.push([+match[1], +(match[2] || match[1])]); 
 

 
     lastIndex = rangeRe.lastIndex; 
 
    } 
 

 
    // Log the original string, the ranges and the remainder 
 
    console.log([ 
 
     str.slice(1), 
 
     ranges.map(function(pair) { 
 
      return pair[0] + '-' + pair[1]; 
 
     }).join(' ; '), 
 
     str.slice(lastIndex) 
 
    ]); 
 
});

这里是我遵循的规则:

  • 数由连续的数字。
  • 范围由一个数字或一对数字组成。
  • 如果一个范围有一对特征,则它们可以用-,~to分开,在分隔符的任一侧加上任意的空格。
  • 范围(备注范围,不是数字)可以用M-STR作为前缀。前缀和范围之间不允许有额外的空格。
  • 范围由,加上,任意一边的任意空格分开。

将每个范围解析为由下限和上限组成的数组对。对于单数范围,两个边界使用相同的值。

我已经使用了exec的状态。循环的每次迭代都开始匹配上次匹配停止的地方。跟踪lastIndex,以便我们可以在最后生成剩余的“随机字符串”。

在我开始之前,我在字符串的前面添加了一个,。这使得RegExp可以假定所有的范围都以,开头,避免了第一范围的特殊情况。

与您发布的某些RegExps的主要区别在于,我将整个“范围分隔符和上限”部分作为一个单元进行了选择,而不是单独使其成为可选项。这样做的结果是,像17 56这样的输入将被视为“随机字符串”而不是作为上限的56。范围将被视为17-17。

+0

这工作非常好。比我原来的解决方案好得多。非常感谢您的努力! – mmuffins

0

因此,获得了咖啡后,我想我找到了接近解决方案的东西:

function extractNumbers(line){ 
    var str = line.replace(/(?:M-\s?|STR)(\d+)/ig,'$1') 
    var rightpart = str.match(/([a-x].*)/i) 
    var leftpart = str.replace(rightpart[1],'') 
    var pattern = /(\d+)\s?(?:-|~|to)?\s?(\d+)?/ig 
    while (result = pattern.exec(leftpart)) {console.log(result)} 
    console.log(rightpart[1]) 
} 

此功能输出的所有号码的范围,然后串到控制台的其余部分。有可能出现误报,因为它首先替换M-和STR后面跟着一个数字的所有出现,即使它们出现在字符串的右边部分。这个确切的字符序列发生在右侧部分的机会可能很小,但仍然...

如果有人对原始问题或如何消除误报机会的想法有一个答案,我会爱上看见了。