JavaScriptでどのように正規表現リテラルを連結できますか?


145

このようなことをすることは可能ですか?

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

または、新しいRegExp()構文を使用して文字列を連結する必要がありますか?コードは自明で簡潔なため、リテラルを使用したいと思います。


2
それはあなたがString.rawを(使用する場合)エスケープ正規表現の文字に対処する方が簡単です:let regexSegment1 = String.raw`\s*hello\s*`
電離層

回答:


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");

2つの正規表現リテラルがある場合、実際には次の手法を使用してそれらを連結できます。

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がリテラル正規表現ではなくリテラル文字列であることだけではなく、より多くの意味を持ちます。


2
このアプローチを使用する場合、各セグメントは有効な正規表現でなければならないことに注意してください。などの式を作成しても機能new RegExp(/(/.source + /.*/.source + /)?/.source);しないようです。
サム

このソリューションは、グループをバックマッチングする場合は機能しません。その場合の有効な解決策については、私の回答をご覧ください。
–MikaëlMayer 2014

charをエスケープする必要がある場合は、二重バックスラッシュを使用します。new Regexp( '\\ $' + "flum")
Jeff Lowery

"<regexp> .flags"を使用する必要がある場合は、フラグにアクセスできるため、理論的にはそれらを組み合わせることもできます。
bnunamak 2017

どこ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 2017年

19

「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のオプションの引数を使用するだけです。

私の状況では、^と$を一緒に連結しようとしているいくつかの式で使用されているという問題が発生します!これらの式は、プログラム全体で使用される文法フィルターです。今、私はそれらのいくつかを一緒に使用して、事前配置のケースを処理したくありません。最初と最後の^(または)$ :)を削除するために、ソースを「スライス」する必要があるかもしれません。乾杯、アレックス。


source-propertyの使用が好きです。あなたは場合-私のような-の使用は、あなたがこのような何かを行う場合、それはガミガミますjslint:var regex = "\.\..*"
ニルス-O-マット

7

問題正規表現に\ 1のようなバックマッチンググループが含まれている場合。

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

次に、汚染源を汚染するだけでは機能しません。実際、この2つの組み合わせは次のとおりです。

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

解決策: 最初に、最初の正規表現で一致するグループの数を数えます。次に、2番目の一致する各トークンについて、一致するグループの数だけトークンをインクリメントします。

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; }
MikaëlMayer

3

リテラル構文をできるだけ頻繁に使用することをお勧めします。短くて読みやすく、エスケープ引用符やダブルエスケープバックラッシュは必要ありません。「Javascript Patterns」より、Stoyan Stefanov 2010。

ただし、連結する唯一の方法はNewの使用です。

評価は避けます。安全ではありません。


1
質問のように分解してコメントすると、複雑な正規表現の方が読みやすくなります。
サム

3

それを提供する:

  • あなたはあなたの正規表現であなたが何をするか知っています;
  • パターンを形成するための正規表現のピースが多数あり、それらは同じフラグを使用します。
  • 小さなパターンのチャンクを配列に分割する方が読みやすいでしょう。
  • また、次の開発者または自分自身のために各パートにコメントできるようにしたい場合。
  • 正規表現を視覚的に単純化する/this/gよりもnew RegExp('this', 'g')
  • 正規表現を最初から1つにまとめるのではなく、追加の手順で組み立ててもかまいません。

次に、次のように記述します。

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>';
})

私の特定のケース(コードミラーのようなエディター)では、次のような多くの置換を行うよりも、1つの大きな正規表現を実行する方がはるかに簡単です。 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

いいえ、文字通りの方法はサポートされていません。RegExpを使用する必要があります。


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

リテラルとRegExpクラスの両方から正規表現ソースを連結できます。

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

1

より簡単な方法は、ソースを連結することです。例:

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

cの値は次のようになります。

/ \ d + \ w + /


-2

両端にをeval('your expression')追加しないため、私は使用することを好みます。//='new RegExp'

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.