全局正则表达式匹配停止中间字符串

时间:2017-09-14 22:59:33

标签: javascript regex

我试图从字符串中提取数字组。 这些数字可以单独使用,也可以作为\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会返回值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返回。

如果有人知道如何解决这个问题,我将不胜感激。

2 个答案:

答案 0 :(得分: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后跟一个数字,即使它们出现在字符串的右边部分。在右边部分出现这种确切字符序列的可能性很小,但仍然是......

如果有人对原始问题有答案或者如何消除误报的可能性,我很乐意看到它。

答案 1 :(得分: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。