Как я могу конкатенировать литералы регулярного выражения в JavaScript?

Можно ли сделать что-то вроде этого?

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

Или мне нужно использовать новый синтаксис RegExp() и конкатенировать строку? Я бы предпочел использовать литерал, поскольку код является более очевидным и кратким.

Ответ 1

Вот как создать регулярное выражение без использования литерального синтаксиса регулярного выражения. Это позволяет вам выполнять произвольные манипуляции со строками до того, как они станут объектом регулярного выражения:

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, являющиеся буквальными строками, а не буквальными регулярными выражениями.

Ответ 2

Просто случайная конкатенация регулярных выражений объектов может иметь некоторые неблагоприятные побочные эффекты. Вместо этого используйте 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

Ответ 3

Я не совсем согласен с опцией "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.

В моей ситуации я запускаю в проблеме ^ и $, которые используются в нескольких выражениях, которые я пытаюсь объединить вместе! Эти выражения представляют собой грамматические фильтры, используемые в рамках программы. Теперь я не буду использовать некоторые из них вместе, чтобы справиться с случаем ПРЕДПОЛОЖЕНИЙ. Возможно, мне придется "срезать" источники, чтобы удалить начальную и конечную ^ (и/или) $:) Привет, Алекс.

Ответ 4

Проблема Если 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

Ответ 5

Обеспечение того, что:

  • вы знаете, что вы делаете в своем регулярном выражении;
  • у вас есть много частей регулярного выражения, чтобы сформировать шаблон, и они будут использовать один и тот же флаг;
  • вы считаете его более читаемым, чтобы разделить ваши маленькие фрагменты паттерна на массив;
  • вы также захотите прокомментировать каждую часть для следующего разработчика или позже:
  • вы предпочитаете визуально упрощать свое регулярное выражение, например /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 (и без хорошего lookbehind, который, к сожалению, не поддерживается в 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>')

Ответ 6

Вам нужно будет использовать новый RegExp! -)

Ответ 7

Нет, буквальный путь не поддерживается. Вам нужно будет использовать RegExp.

Ответ 8

Было бы предпочтительнее использовать литеральный синтаксис как можно чаще. Это короче, более разборчиво, и вам не нужны кавычки с вылетом или двойные зазоры. Из "Javascript Patterns", Стоян Стефанов 2010.

Но использование New может быть единственным способом конкатенации.

Я бы избегал eval. Это небезопасно.

Ответ 9

Используйте конструктор с двумя параметрами и избегайте проблемы с завершающим "/":

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

Ответ 10

Вы можете сделать что-то вроде:

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

Отрезками будут строки (а не литералы регулярных выражений), переданные в виде отдельных аргументов.

Ответ 11

Вы можете объединить источник регулярных выражений как из литерала, так и из класса RegExp:

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

Ответ 12

Я предпочитаю использовать eval('your expression'), потому что он не добавляет / на каждом конце /, который делает ='new RegExp'.