JavaScript正则表达式中的多个嵌套匹配项

时间:2017-02-17 00:25:45

标签: javascript regex

尝试编写正则表达式以匹配GS1条形码模式(https://en.wikipedia.org/wiki/GS1-128),其中包含2个或更多这些模式,这些模式具有标识符,后跟一定数量的数据字符。

我需要匹配此条形码,因为它包含2个标识符和数据模式:

人类可读的parens中的标识符:(01)12345678901234(17)501200

实际数据:011234567890123417501200

中只有一个模式时,不应匹配此条形码

人类可读:(01)12345678901234

实际数据:0112345678901234

以下情况似乎应该有效:

var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6})){2,}/g;
var str = "011234567890123417501200";

console.log(str.replace(regex, "$4"));
// matches 501200
console.log(str.replace(regex, "$1"));
// no match? why?

出于某种奇怪的原因,只要我删除{2,}它就可以了,但是我需要{2,},这样只有在有多个匹配时它才会返回匹配。

// Remove {2,} and it will return the first match
var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6}))/g;
var str = "011234567890123417501200";

console.log(str.replace(regex, "$4"));
// matches 501200
console.log(str.replace(regex, "$1"));
// matches 12345678901234
// but then the problem is it would also match single identifiers such as
var str2 = "0112345678901234";
console.log(str2.replace(regex, "$1"));
 

如何使这项工作如此,如果有超过1组匹配组,它只会匹配并提取数据?

谢谢!

3 个答案:

答案 0 :(得分:2)

对于Perl兼容的正则表达式(PCRE),您的RegEx在逻辑上和语法上都是正确的。我认为您面临的问题是JavaScript存在重复捕获组的问题。这就是为什么一旦取出{2,},RegEx就能正常工作。通过添加量词,JavaScript将确保仅返回 最后一场比赛。

我建议删除{2,}量词,然后以编程方式检查匹配项。我知道这对于那些忠实于RegEx的人来说并不理想,但 c' est la vie

请参阅下面的代码:



var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6}))/g;
var str = "011234567890123417501200";

// Check to see if we have at least 2 matches.
var m = str.match(regex);
console.log("Matches list: " + JSON.stringify(m));
if (m.length < 2) {
    console.log("We only received " + m.length + " matches.");
} else {
    console.log("We received " + m.length + " matches.");
    console.log("We have achieved the minimum!");
}

// If we exec the regex, what would we get?
console.log("** Method 1 **");
var n;
while (n = regex.exec(str)) {
    console.log(JSON.stringify(n));
}

// That's not going to work.  Let's try using a second regex.
console.log("** Method 2 **");
var regex2 = /^(\d{2})(\d{6,})$/;
var arr = [];
var obj = {};
for (var i = 0, len = m.length; i < len; i++) {
    arr = m[i].match(regex2);
    obj[arr[1]] = arr[2];
}

console.log(JSON.stringify(obj));

// EOF
&#13;
&#13;
&#13;

我希望这会有所帮助。

答案 1 :(得分:1)

原因是捕获组仅提供该特定组的最后一场比赛。想象一下,序列中有两个条形码具有相同的标识符01 ...现在很明显$1不能同时引用这两个条形码。捕获组仅保留第二次出现。

一种直截了当但不那么优雅的方法是删除{2,},而是重复整个正则表达式模式以匹配第二个条形码序列。我认为你还需要使用^(字符串锚的开头)来确保匹配位于字符串的开头,否则你可能会在无效序列的中途选择一个标识符。在重复的正则表达式模式之后,如果你想忽略第二个序列后面的任何内容,你还应该添加.*,而不是在使用replace时让它回复给你。

最后,由于您不知道第一场和第二场比赛将找到哪个标识符,因此您需要在$1$2$3$4中重现replace,因为您知道这四个中只有一个会是一个非空字符串。第二场比赛相同:$5$6$7$8

以下是应用于示例字符串的改进代码:

&#13;
&#13;
var regex = /^(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6}))(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6})).*/;

var str = "011234567890123417501200";
console.log(str.replace(regex, "$1$2$3$4")); // 12345678901234
console.log(str.replace(regex, "$5$6$7$8")); // 501200
&#13;
&#13;
&#13;

如果你还需要匹配第二个条形码,那么你就无法摆脱写一个循环。仅使用基于replace的正则表达式,您无法做到这一点。

带循环

如果允许循环,则可以使用regex#exec方法。然后,我建议在正则表达式中添加一种&#34; catch all&#34;,如果其他标识符都不匹配,它将匹配一个字符。如果在循环中你发现了这样一个&#34;赶上所有&#34;匹配,退出:

&#13;
&#13;
var str = "011234567890123417501200";
var regex = /(?:01(\d{14})|10([^\x1D]{6,20})|11(\d{6})|17(\d{6})|(.))/g;
//              1: ^^^^^^  2: ^^^^^^^^^^^^^  3: ^^^^^  4: ^^^^^ 5:^ (=failure)
var result = [], grp;
while ((grp = regex.exec(str)) && !grp[5]) result.push(grp.slice(1).join(''));

// Consider it a failure when not at least 2 matched.
if (result.length < 2) result = [];
console.log(result);
&#13;
&#13;
&#13;

答案 2 :(得分:0)

更新

第一个例子

示例$ 1 $ 2 $ 3 $ 4不知道为什么在矩阵:)

但你看到$ 1 - &gt; ABC $ 2 - &gt; def $ 3 - &gt; ghi $ 4 - &gt; JKL

&#13;
&#13;
//              $1   $2     $3  $4
var regex = /(abc)|(def)|(ghi)|(jkl)/g;
var str = "abcdefghijkl";

// test  
console.log(str.replace(regex, "$1 1st "));
console.log(str.replace(regex, "$2 2nd "));
console.log(str.replace(regex, "$3 3rd "));
console.log(str.replace(regex, "$4 4th "));
&#13;
&#13;
&#13;

第二个例子

在这里混合有缺陷的

&#13;
&#13;
//              $1   $2     $3  $4
var regex = /((abc)|(def)|(ghi)|(jkl)){2,}/g;
var str = "abcdefghijkl";

// test  
console.log(str.replace(regex, "$1 1st "));
console.log(str.replace(regex, "$2 2nd "));
console.log(str.replace(regex, "$3 3rd "));
console.log(str.replace(regex, "$4 4th "));
&#13;
&#13;
&#13;

如您所见,($4)( )( )( )代替($1)( )( )( )

如果我认为问题恰到好处,那么问题就在于外部括号()令人困惑&#39;伪&#39; 1美元是4美元。如果您在外括号()中有一个模式,然后{2,},那么在外括号()中它是$ 4但在子模式中有(?:01(\d{14}))但它看起来不是$ 1但是有缺陷在这种情况下4美元。也许这会导致外部括号()中记住的值与第一个记住的值之间的冲突,但在括号(这是$ 1)中。这就是为什么它没有显示出来的原因。换句话说,你有($ 4($ 1 $ 2 $ 3 $ 4)),这是不正确的。

我添加图片以显示我的意思。

enter image description here

正如@Damian所说

  

通过添加量词,JavaScript肯定只会返回最后一场比赛。

所以4美元是最后一场比赛。

结束更新

我添加了有用的小测试

&#13;
&#13;
var regex = /(?:01(\d{14})|10(\x1D{6,20})|11(\d{6})|17(\d{6})){2,}/g;
var str = "011234567890123417501200";

// test
console.log(str.replace(regex, "$1 1st "));
console.log(str.replace(regex, "$2 2nd "));
console.log(str.replace(regex, "$3 3rd "));
console.log(str.replace(regex, "$4 4th "));
&#13;
&#13;
&#13;