如何在JavaScript中连接正则表达式文字?


145

可以做这样的事情吗?

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

还是我必须使用新的RegExp()语法并连接一个字符串?我更喜欢使用文字,因为代码既不言而喻又简洁。


2
如果使用String.raw()let regexSegment1 = String.raw`\s*hello\s*`
则更

Answers:


190

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

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

这比仅将表达式一和两个作为文字字符串而不是文字正则表达式要复杂得多。


2
请记住,使用此方法时,每个段都必须是有效的正则表达式。构造这样的表达式new RegExp(/(/.source + /.*/.source + /)?/.source);似乎不起作用。
山姆

对于反向匹配组,此解决方案不起作用。在这种情况下,请参阅我的答案以获取可行的解决方案。
麦克尔迈耶

如果您需要转义一个字符,则使用双反斜杠:new Regexp('\\ $'+“ flum”)
Jeff Lowery

如果必须使用“ <regexp> .flags”,则可以访问这些标志,因此理论上也可以将它们组合在一起。
bnunamak

你从哪里来expression_one?你是说regex1
TallOrderDev

30

只是随机连接正则表达式对象会产生一些不利的副作用。改用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


可以使用RegExp.prototype.flags
Dmitry Parzhitsky

19

我不太同意“评估”选项。

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的可选参数即可。

在我的情况下,我确实遇到了^和$的问题,我试图将它们连接在一起使用多个表达式!这些表达式是程序中使用的语法过滤器。现在,我将不使用它们中的一些来处理PREPOSITIONS的情况。我可能不得不“分割”源代码以删除开始和结束的^(和/或)$ :)干杯,Alex。


我喜欢使用源属性。如果您-像我一样-使用jslint,则执行以下操作将会na:var regex = "\.\..*"
Nils-o-mat

7

问题如果正则表达式包含\ 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

2
是的(虽然我不会在这里修改它)。此功能是关联的,所以你可以使用下面的代码:function concatenateList() { var res = arguments[0]; for(var i = 1; i < arguments.length; i++) { res = concatenate(res, arguments[i]); } return res; }
麦克尔迈耶

3

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

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

我会避免评估。它不安全。


1
我认为复杂的正则表达式在分解并像问题中一样注释时更容易理解。
山姆

3

提供了:

  • 你知道你在正则表达式中做什么;
  • 您有很多正则表达式片断形成一个模式,它们将使用相同的标志;
  • 您发现将小图案块分成一个数组更易读;
  • 您还希望能够为下一个开发人员或以后自己注释每个部分;
  • 您宁愿在视觉上简化正则表达式/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>')

2

您可以执行以下操作:

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

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



1

将构造函数与2个参数一起使用,并避免在结尾加上'/'的问题:

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


1

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

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

c值将导致:

/ \ d + \ w + /


-2

我更喜欢使用eval('your expression'),因为它不添加/的每一端/='new RegExp'一样。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.