Как я могу объединить литералы регулярных выражений в JavaScript? - PullRequest
121 голосов
/ 09 октября 2008

Можно ли сделать что-то подобное?

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

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

Ответы [ 11 ]

170 голосов
/ 09 октября 2008

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

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

Это просто более многословно, чем просто иметь выражение одно и два, являющиеся буквальными строками вместо буквальных регулярных выражений.

21 голосов
/ 20 марта 2014

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

13 голосов
/ 14 января 2010

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

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

6 голосов
/ 28 ноября 2014

Проблема Если регулярное выражение содержит группы с обратным соответствием, такие как \ 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
3 голосов
/ 26 января 2017

При условии, что:

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

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

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

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

1 голос
/ 23 апреля 2014

Используйте конструктор с 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 голос
/ 25 июля 2012

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

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

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

1 голос
/ 09 октября 2008

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

1 голос
/ 09 октября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...