如何在 JavaScript 中连接 regex 文本?

有可能做这样的事吗?

var pattern = /some regex segment/ + /* comment here */
/another segment/;

还是必须使用新的 RegExp()语法并连接字符串?我更喜欢使用文字,因为代码更加明显和简洁。

122505 次浏览

下面介绍如何在不使用正则表达式文本语法的情况下创建正则表达式。这使您可以在字符串变成正则表达式对象之前进行任意字符串操作:

var segment_part = "some bit of the regexp";
var pattern = new RegExp("some regex segment" + /*comment here */
segment_part + /* that was defined just now */
"another segment");

如果有两个正则表达式文字,实际上可以使用以下技术将它们连接起来:

var regex1 = /foo/g;
var regex2 = /bar/y;
var flags = (regex1.flags + regex2.flags).split("").sort().join("").replace(/(.)(?=.*\1)/g, "");
var regex3 = new RegExp(expression_one.source + expression_two.source, flags);
// regex3 is now /foobar/gy

只是比仅仅使用表达式1和表达式2作为字符串而不是字面正则表达式更加冗长。

不,不支持文字方式。您必须使用 RegExp。

我更喜欢使用 eval('your expression'),因为它不像 ='new RegExp'那样在每个 end/上添加 /

我不太同意“ Eval”这个选项。

var xxx = /abcd/;
var yyy = /efgh/;
var zzz = new RegExp(eval(xxx)+eval(yyy));

将给出“//abcd//efgh//”,这不是预期的结果。

使用类似

var zzz = new RegExp(xxx.source+yyy.source);

将给出“/abcdefgh/”,这是正确的。

逻辑上没有必要评估,你知道你的表达式。您只需要它的源代码,或者如何编写它,而不一定需要它的价值。至于标志,您只需要使用 RegExp 的可选参数。

在我的情况下,我运行的问题 ^ 和 $被用在几个表达式中,我试图连接在一起!这些表达式是整个程序中使用的语法过滤器。现在我不想把它们中的一些放在一起来处理介词的情况。 我可能必须“分割”源代码以删除开始和结束的 ^ (和/或) $:) 干杯,亚历克斯。

最好尽可能多地使用文字语法。它更短,更易读,而且您不需要转义引号或双转义反冲。来自“ Javascript 模式”,Stoyan Stefanov 2010。

但是使用 New 可能是连接的唯一方法。

我会避开 Eval 的,那不安全。

只是随机连接正则表达式 物品可能会有一些不利的副作用。使用 RegExp.source代替:

var r1 = /abc/g;
var r2 = /def/;
var r3 = new RegExp(r1.source + r2.source,
(r1.global ? 'g' : '')
+ (r1.ignoreCase ? 'i' : '') +
(r1.multiline ? 'm' : ''));
console.log(r3);
var m = 'test that abcdef and abcdef has a match?'.match(r3);
console.log(m);
// m should contain 2 matches

这还将使您能够使用标准的 RegExp 标志从以前的 RegExp 保留正则表达式标志。

JsFiddle

使用带有两个参数的构造函数,避免尾随’/’的问题:

var re_final = new RegExp("\\" + ".", "g");    // constructor can have 2 params!
console.log("...finally".replace(re_final, "!") + "\n" + re_final +
" works as expected...");                  // !!!finally works as expected


// meanwhile


re_final = new RegExp("\\" + "." + "g");              // appends final '/'
console.log("... finally".replace(re_final, "!"));    // ...finally
console.log(re_final, "does not work!");              // does not work

问题 如果 regexp 包含像1这样的反向匹配组。

var r = /(a|b)\1/  // Matches aa, bb but nothing else.
var p = /(c|d)\1/   // Matches cc, dd but nothing else.

那么,仅仅列举消息来源是行不通的。实际上,两者的结合是:

var rp = /(a|b)\1(c|d)\1/
rp.test("aadd") // Returns false

解决办法: 首先计算第一个正则表达式中匹配组的数目,然后对于第二个正则表达式中的每个反向匹配标记,将其增加匹配组的数目。

function concatenate(r1, r2) {
var count = function(r, str) {
return str.match(r).length;
}
var numberGroups = /([^\\]|^)(?=\((?!\?:))/g; // Home-made regexp to count groups.
var offset = count(numberGroups, r1.source);
var escapedMatch = /[\\](?:(\d+)|.)/g;        // Home-made regexp for escaped literals, greedy on numbers.
var r2newSource = r2.source.replace(escapedMatch, function(match, number) { return number?"\\"+(number-0+offset):match; });
return new RegExp(r1.source+r2newSource,
(r1.global ? 'g' : '')
+ (r1.ignoreCase ? 'i' : '')
+ (r1.multiline ? 'm' : ''));
}

测试:

var rp = concatenate(r, p) // returns  /(a|b)\1(c|d)\2/
rp.test("aadd") // Returns true

前提是:

  • 您知道在 regexp 中做什么;
  • 你有许多正则表达式片段形成一个模式,他们将使用相同的标志;
  • 你会发现把你的小模式块分成一个数组更容易阅读;
  • 您还希望能够为下一个开发人员或自己注释每个部分以后;
  • 您更喜欢在视觉上简化您的正则表达式,如 /this/g而不是 new RegExp('this', 'g');
  • 您可以在额外的步骤中组装正则表达式,而不是从一开始就将其完整地组装起来;

那么你可以这样写:

var regexParts =
[
/\b(\d+|null)\b/,// Some comments.
/\b(true|false)\b/,
/\b(new|getElementsBy(?:Tag|Class|)Name|arguments|getElementById|if|else|do|null|return|case|default|function|typeof|undefined|instanceof|this|document|window|while|for|switch|in|break|continue|length|var|(?:clear|set)(?:Timeout|Interval))(?=\W)/,
/(\$|jQuery)/,
/many more patterns/
],
regexString  = regexParts.map(function(x){return x.source}).join('|'),
regexPattern = new RegExp(regexString, 'g');

你可以这样做:

string.replace(regexPattern, function()
{
var m = arguments,
Class = '';


switch(true)
{
// Numbers and 'null'.
case (Boolean)(m[1]):
m = m[1];
Class = 'number';
break;


// True or False.
case (Boolean)(m[2]):
m = m[2];
Class = 'bool';
break;


// True or False.
case (Boolean)(m[3]):
m = m[3];
Class = 'keyword';
break;


// $ or 'jQuery'.
case (Boolean)(m[4]):
m = m[4];
Class = 'dollar';
break;


// More cases...
}


return '<span class="' + Class + '">' + m + '</span>';
})

在我的特殊情况下(一个类似代码镜像的编辑器) ,执行一个大的正则表达式要容易得多,而不是像下面这样每次用 html 标记替换来包装一个表达式时,下一个模式将更难定位而不影响 html 标记本身(而且不幸的是,没有好的 往后看在 javascript 中不支持) :

.replace(/(\b\d+|null\b)/g, '<span class="number">$1</span>')
.replace(/(\btrue|false\b)/g, '<span class="bool">$1</span>')
.replace(/\b(new|getElementsBy(?:Tag|Class|)Name|arguments|getElementById|if|else|do|null|return|case|default|function|typeof|undefined|instanceof|this|document|window|while|for|switch|in|break|continue|var|(?:clear|set)(?:Timeout|Interval))(?=\W)/g, '<span class="keyword">$1</span>')
.replace(/\$/g, '<span class="dollar">$</span>')
.replace(/([\[\](){}.:;,+\-?=])/g, '<span class="ponctuation">$1</span>')

你可以这样做:

function concatRegex(...segments) {
return new RegExp(segments.join(''));
}

这些段将是字符串(而不是正则表达式文本)作为单独的参数传入。

您可以从文字类和 RegExp 类中连接正则表达式源:

var xxx = new RegExp(/abcd/);
var zzz = new RegExp(xxx.source + /efgh/.source);

对我来说,更简单的方法是将源头连接起来,例如:

a = /\d+/
b = /\w+/
c = new RegExp(a.source + b.source)

C 值将导致:

/d + w +/