Преобразование пользовательской строки ввода в регулярное выражение

333

Я разрабатываю тестер регулярных выражений в HTML и JavaScript. Пользователь вводит регулярное выражение, строку и выбирает функцию, которую он хочет проверить (например, поиск, сопоставление, замена и т. Д.), С помощью переключателя, и программа отображает результаты, когда эта функция запускается с указанными аргументами. Естественно, будут дополнительные текстовые поля для дополнительных аргументов для замены и тому подобное.

Моя проблема заключается в получении строки от пользователя и превращении ее в регулярное выражение. Если я скажу, что им не нужно иметь //символы, которые они вводят в регулярном выражении, тогда они не могут устанавливать флаги, как gи i. Таким образом, они должны иметь //символы вокруг выражения, но как я могу преобразовать эту строку в регулярное выражение? Он не может быть литералом, поскольку он является строкой, и я не могу передать его конструктору RegExp, поскольку он не является строкой без //символа s. Есть ли другой способ сделать пользовательскую строку ввода в регулярное выражение? Придется ли мне анализировать строку и флаги регулярного выражения //и затем конструировать их по-другому? Нужно ли им вводить строку, а затем вводить флаги отдельно?

Гордон Густафсон
источник

Ответы:

611

Используйте конструктор объекта RegExp для создания регулярного выражения из строки:

var re = new RegExp("a|b", "i");
// same as
var re = /a|b/i;
гумбо
источник
1
было бы неплохо иметь онлайн-инструмент с полем ввода
holms
61
Делая это таким образом, вы должны избегать обратной косой черты, напримерvar re = new RegExp("\\w+");
JD Smith
12
@holms regex101.com - отличный онлайн-инструмент для регулярных выражений
Fran Herrero
2
Мне потребовалось некоторое время, чтобы понять, что не нужно использовать косые черты
Gerfried
2
@JDSmith Я не имел в виду это в вашем примере. Я имел в виду, что вам нужно избегать двойных кавычек, если вы хотите, чтобы они были частью регулярного выражения при условии, что они жестко закодированы. Очевидно, что ничего из этого не применимо, если строка находится в переменной, как в <input>HTML-теге. var re = new RegExp("\"\\w+\"");является примером жестких кодированными с использованием регулярных выражений конструктора RegExp и избежать двойных кавычек это необходимо. Под строкой в ​​переменной я подразумеваю, что вы можете просто делать var re = new RegExp(str);и strсодержать двойные кавычки или обратную косую черту без проблем.
Луис Пауло
66
var flags = inputstring.replace(/.*\/([gimy]*)$/, '$1');
var pattern = inputstring.replace(new RegExp('^/(.*?)/'+flags+'$'), '$1');
var regex = new RegExp(pattern, flags);

или

var match = inputstring.match(new RegExp('^/(.*?)/([gimy]*)$'));
// sanity check here
var regex = new RegExp(match[1], match[2]);
анонимное
источник
Вы должны учитывать, что неверный ввод, как /\/распознается.
Гамбо
8
Или позволить конструктору RegExp завершиться с ошибкой, "завершая \ в регулярном выражении", вместо написания сложного парсера.
Аноним
21

Вот одна строка: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

Я получил это из модуля NPM escape-string-regexp .

Опробовать это:

escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
function escapeStringRegExp(str) {
    return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
}

console.log(new RegExp(escapeStringRegExp('example.com')));
// => /example\.com/

Использование теговых литералов шаблона с поддержкой флагов:

function str2reg(flags = 'u') {
    return (...args) => new RegExp(escapeStringRegExp(evalTemplate(...args))
        , flags)
}

function evalTemplate(strings, ...values) {
    let i = 0
    return strings.reduce((str, string) => `${str}${string}${
        i < values.length ? values[i++] : ''}`, '')
}

console.log(str2reg()`example.com`)
// => /example\.com/u
Rivenfall
источник
15

Используйте конструктор объектов JavaScript RegExp .

var re = new RegExp("\\w+");
re.test("hello");

Вы можете передать флаги как второй строковый аргумент в конструктор. Смотрите документацию для деталей.

Айман Хуриех
источник
9

В моем случае пользовательский ввод иногда был разделен разделителями, а иногда нет. поэтому я добавил еще один случай ..

var regParts = inputstring.match(/^\/(.*?)\/([gim]*)$/);
if (regParts) {
    // the parsed pattern had delimiters and modifiers. handle them. 
    var regexp = new RegExp(regParts[1], regParts[2]);
} else {
    // we got pattern string without delimiters
    var regexp = new RegExp(inputstring);
}
staabm
источник
3
Вы всегда можете использовать .split()функцию вместо длинной строки регулярного выражения. regParts = inputstring.split('/')это сделало бы regParts[1]строку регулярного выражения и regParts[2]разделители (при условии, что настройка регулярного выражения равна /.../gim). Вы можете проверить, есть ли разделители с regParts[2].length < 0.
Jaketr00
3

Я предлагаю вам также добавить отдельные флажки или текстовое поле для специальных флагов. Таким образом, ясно, что пользователю не нужно добавлять какие //- либо . В случае замены предоставьте два текстовых поля. Это сделает вашу жизнь намного проще.

Зачем? Потому что в противном случае некоторые пользователи будут добавлять //, а другие нет. А некоторые сделают синтаксическую ошибку. Затем, после того, как вы удалите //их, вы можете получить синтаксически правильное регулярное выражение, которое не похоже на то, что задумал пользователь, что ведет к странному поведению (с точки зрения пользователя).

Stephan202
источник
2

Это также будет работать, когда строка недопустима или не содержит флагов и т. Д.

function regExpFromString(q) {
  let flags = q.replace(/.*\/([gimuy]*)$/, '$1');
  if (flags === q) flags = '';
  let pattern = (flags ? q.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1') : q);
  try { return new RegExp(pattern, flags); } catch (e) { return null; }
}

console.log(regExpFromString('\\bword\\b'));
console.log(regExpFromString('\/\\bword\\b\/gi'));
            

kofifus
источник
2

Если вы действительно хотите преобразовать строку в регулярное выражение, попробуйте использовать следующую функцию:

function String2Regex(s){return new RegExp(s.match(/\/(.+)\/.*/)[1], s.match(/\/.+\/(.*)/)[1]);}

Вы можете использовать это так:

"abc".match(String2Regex("/a/g"))
> ["a"]

Для справки, вот отформатированная и более современная версия:

const String2Regex = str => {
  // Main regex
  const main = str.match(/\/(.+)\/.*/)[1]

  // Regex options
  const options = str.match(/\/.+\/(.*)/)[1]

  // Return compiled regex
  return new RegExp(main, options)
}
Ричи Бендалл
источник
1

Благодаря более ранним ответам, эти блоки служат универсальным решением для применения настраиваемой строки в RegEx .. для фильтрации текста:

var permittedChars = '^a-z0-9 _,.?!@+<>';
permittedChars = '[' + permittedChars + ']';

var flags = 'gi';
var strFilterRegEx = new RegExp(permittedChars, flags);

log.debug ('strFilterRegEx: ' + strFilterRegEx);

strVal = strVal.replace(strFilterRegEx, '');
// this replaces hard code solt:
// strVal = strVal.replace(/[^a-z0-9 _,.?!@+]/ig, '');
Джин Бо
источник
1

Вы можете запросить флаги с помощью флажков, а затем сделать что-то вроде этого:

var userInput = formInput;
var flags = '';
if(formGlobalCheckboxChecked) flags += 'g';
if(formCaseICheckboxChecked) flags += 'i';
var reg = new RegExp(userInput, flags);
Пим Ягер
источник
Похоже, что в RegEx отсутствует конечная буква p . Стек не позволяет мне редактировать 1 символ
Джин Бо
-3

Я использую, evalчтобы решить эту проблему.

Например:

    function regex_exec() {

        // Important! Like @Samuel Faure mentioned, Eval on user input is a crazy security risk, so before use this method, please take care of the security risk. 
        var regex = $("#regex").val();

        // eval()
        var patt = eval(userInput);

        $("#result").val(patt.exec($("#textContent").val()));
    }
Playhi
источник
3
eval on userInput - безумная угроза безопасности
Сэмюэль Форе
1
мистер Бобби таблицы!
Луис Фелипе