web-dev-qa-db-ja.com

JavaScriptで正規表現リテラルを連結するにはどうすればよいですか?

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

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

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

127
eyelidlessness

正規表現リテラル構文を使用せずに正規表現を作成する方法を次に示します。これにより、正規表現オブジェクトになる前に任意の文字列操作を行うことができます。

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がリテラルの正規表現ではなくリテラル文字列であるだけでなく、より冗長です。

175
Jerub

正規表現をランダムに連結するだけ オブジェクト いくつかの副作用があります。代わりに 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' : ''));
var m = 'test that abcdef and abcdef has a match?'.match(r3);
// m should contain 2 matches

これにより、標準のRegExpフラグを使用して、以前のRegExpの正規表現フラグを保持することもできます。

jsFiddle

22
Japheth Salva

「評価」オプションにはまったく同意しません。

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 /」が得られますが、これは正しいです。

論理的に評価する必要はありません、あなたはあなたの表現を知っています。必要なのはそのSOURCEまたはその記述方法だけであり、必ずしもその値ではありません。フラグについては、RegExpのオプションの引数を使用するだけです。

私の状況では、^と$が連結しようとしているいくつかの式で使用されているという問題に直面しています。これらの式は、プログラム全体で使用される文法フィルターです。今、私はそれらのいくつかを一緒に使用してPREPOSITIONSのケースを処理したくありません。開始と終了の^(および/または)$を削除するには、ソースを「スライス」する必要があるかもしれません:)乾杯、アレックス。

15
Alex

問題正規表現に\ 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
7
Mikaël Mayer

それを提供する:

  • 正規表現で何をするか知っています。
  • パターンを形成するために多くの正規表現があり、それらは同じフラグを使用します。
  • 小さなパターンチャンクを配列に分割すると読みやすくなります。
  • また、次の開発者または後で自分のために各部分をコメントできるようにしたい。
  • new RegExp('this', 'g')ではなく/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>';
})

私の特定のケース(コードミラーのようなエディタ)では、式をラップするhtmlタグで置き換えるたびに次のような多くの置き換えを行うよりも、1つの大きな正規表現を実行する方がはるかに簡単です、次のパターンはhtmlタグ自体に影響を与えることなく(そして残念ながらjavascriptでサポートされていないlookbehindなしで)ターゲティングするのが難しくなります:

.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>')
4
antoni

次のようなことができます:

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

セグメントは、別個の引数として渡される文字列(正規表現リテラルではなく)になります。

2
Neil Strain

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

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

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

2
Jonathan Wright

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
2
ph7

新しいRegExpを使用する必要があります!-)

2
roenving

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

2
Aupajo