Перетворення ряду введення користувача в регулярний вираз


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
Мені знадобилося деякий час, щоб побачити, що не потрібно потрібних
косої косої

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

Слід врахувати, що недійсний вхід, як-от /\/розпізнається.
Gumbo

8
Або нехай конструктор RegExp виходить з ладу, "зачіпаючи \ у регулярному виразі", замість того, щоб написати складний аналізатор.
Анонім

21

Ось одноколісний: str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')

Я отримав це з модуля escape-string-regexp NPM.

Спробуйте:

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


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

3
Ви завжди можете використовувати .split()функцію замість довгого рядка. regParts = inputstring.split('/')це зробить regParts[1]рядок регулярних виразів та regParts[2]роздільники (якщо припустимо встановити регулярний вираз /.../gim). Ви можете перевірити, чи є роздільники regParts[2].length < 0.
Jaketr00

3

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

Чому? Тому що в іншому випадку деякі користувачі додадуть //s, а інші не. А деякі зроблять синтаксичну помилку. Потім, після того, як ви знімете зображення //, ви можете закінчитись синтаксично правильним регулярним виразом, який не схожий на те, що призначений користувачем, що призводить до дивної поведінки (з точки зору користувача).


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


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 символу
Gene Bo

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

3
eval on userInput - це шалений ризик для безпеки
Samuel Faure

1
міс Боббі столи!
Луїз Феліпе
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.