Есть ли функция RegExp.escape в Javascript?
Я просто хочу создать регулярное выражение из любой возможной строки.
var usersString = "Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches = "Hello".match(expression);
Есть ли встроенный метод для этого? Если нет, что люди используют? Руби имеет RegExp.escape
, Я не чувствую, что мне нужно писать свое, должно быть что-то стандартное. Спасибо!
19 ответов
Связанная выше функция недостаточна. Не удается сбежать ^
или же $
(начало и конец строки), или -
, который в группе символов используется для диапазонов.
Используйте эту функцию:
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
Хотя на первый взгляд это может показаться ненужным, -
(так же как ^
) делает функцию подходящей для экранирования символов для вставки в класс символов, а также в тело регулярного выражения.
Спасаясь /
делает функцию подходящей для экранирования символов, которые будут использоваться в литерале регулярного выражения JS для последующего вычисления.
Так как нет побочных эффектов от выхода из них, имеет смысл сбежать, чтобы охватить более широкие варианты использования.
И да, досадно, что это не является частью стандартного JavaScript.
Для тех, кто использует lodash, начиная с версии 3.0.0 встроена функция _.escapeRegExp:
_.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)'
И, если вам не нужна полная библиотека lodash, вам может потребоваться только эта функция!
Большинство выражений здесь решают отдельные конкретные случаи использования.
Это нормально, но я предпочитаю подход "всегда работает".
function regExpEscape(literal_string) {
return literal_string.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&');
}
Это "полностью экранирует" буквальную строку для любого из следующих применений в регулярных выражениях:
- Вставка в регулярное выражение. Например
new RegExp(regExpEscape(str))
- Вставка в класс персонажа. Например
new RegExp('[' + regExpEscape(str) + ']')
- Вставка в спецификатор целого числа. Например
new RegExp('x{1,' + regExpEscape(str) + '}')
- Выполнение в механизмах регулярных выражений, отличных от JavaScript.
Охваченные специальные символы:
-
: Создает диапазон символов в классе символов.[
/]
: Запуск / окончание класса персонажа.{
/}
: Запускает / заканчивает спецификатор нумерации.(
/)
: Запуск / окончание группы.*
/+
/?
: Указывает тип повторения..
: Соответствует любому персонажу.\
: Экранирует персонажей и запускает сущности.^
: Определяет начало зоны сопоставления и отменяет сопоставление в классе символов.$
: Указывает конец соответствующей зоны.|
: Определяет чередование.#
: Указывает комментарий в режиме свободного пробела.\s
: Игнорируется в режиме свободного пространства.,
: Разделяет значения в спецификаторе нумерации./
: Начинается или заканчивается выражение.:
: Завершает специальные типы групп и часть классов символов в стиле Perl.!
: Отрицает группу нулевой ширины.<
/=
: Часть групповых спецификаций нулевой ширины.
Заметки:
/
не является строго необходимым в любой разновидности регулярного выражения. Тем не менее, это защищает в случае, если кто-то (дрожь) делаетeval("/" + pattern + "/");
,,
гарантирует, что если строка должна быть целым числом в числовом спецификаторе, она будет правильно вызывать ошибку компиляции RegExp вместо того, чтобы молча компилировать неправильно.#
, а также\s
не нужно экранировать в JavaScript, но делают во многих других вариантах. Они здесь экранированы на случай, если регулярное выражение будет позже передано другой программе.
Если вам также необходимо защитить регулярное выражение от потенциальных добавлений к возможностям механизма регулярных выражений JavaScript, я рекомендую использовать более параноидальный:
function regExpEscapeFuture(literal_string) {
return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}
Эта функция экранирует все символы, кроме тех, которые явно гарантированно не будут использоваться для синтаксиса в будущих вариантах регулярных выражений.
Для истинных любителей санитарии рассмотрим этот крайний случай:
var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')');
Это должно хорошо скомпилироваться в JavaScript, но не будет в некоторых других вариантах. Если намереваетесь перейти на другой аромат, нулевой регистр s === ''
должны быть независимо проверены, например, так:
var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');
Руководство по регулярным выражениям в Mozilla Developer Network предоставляет следующую функцию:
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
В виджете автозаполнения jQueryUI (версия 1.9.1) они используют немного другое регулярное выражение (строка 6753), вот регулярное выражение в сочетании с подходом @bobince.
RegExp.escape = function( value ) {
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
Существует предложение ES7 для RegExp.escape по адресу https://github.com/benjamingr/RexExp.escape/, а полифилл доступен по адресу https://github.com/ljharb/regexp.escape.
Ничто не должно мешать вам просто экранировать каждый не алфавитно-цифровой символ:
usersString.replace(/(?=\W)/g, '\\');
Вы теряете определенную степень читабельности при выполнении re.toString()
но вы выиграли много простоты (и безопасности).
Согласно ECMA-262, с одной стороны, регулярные выражения "синтаксические символы" всегда не алфавитно-цифровые, так что результат является безопасным, а специальные escape-последовательности (\d
, \w
, \n
) всегда являются буквенно-цифровыми, так что не будет произведено ложных управляющих выходов.
Предложение ES7 для RegExp.escape доступно по адресу https://github.com/benjamingr/RexExp.escape/, а полифилл доступен по адресу https://github.com/ljharb/regexp.escape.
Пример, основанный на отклоненном предложении ES, включает проверки, существует ли уже свойство, в случае, если TC39 отступает от своего решения.
Код:
if (!Object.prototype.hasOwnProperty.call(RegExp, 'escape')) {
RegExp.escape = function(string) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
// https://github.com/benjamingr/RegExp.escape/issues/37
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
};
}
Код уменьшен:
Object.prototype.hasOwnProperty.call(RegExp,"escape")||(RegExp.escape=function(e){return e.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")});
// ...
var assert = require('assert');
var str = 'hello. how are you?';
var regex = new RegExp(RegExp.escape(str), 'g');
assert.equal(String(regex), '/hello\. how are you\?/g');
Также есть
npm
модуль по адресу:https://www.npmjs.com/package/regexp.escape
Его можно установить и использовать так:
npm install regexp.escape
или же
yarn add regexp.escape
var escape = require('regexp.escape');
var assert = require('assert');
var str = 'hello. how are you?';
var regex = new RegExp(escape(str), 'g');
assert.equal(String(regex), '/hello\. how are you\?/g');
На странице GitHub && NPM также есть описание того, как использовать прокладку / полифилл для этой опции. Эта логика основана на
return RegExp.escape || implementation;
, где реализация содержит использованное выше регулярное выражение.
Модуль NPM - это дополнительная зависимость, но он также упрощает внешнему участнику идентификацию логических частей, добавленных в код. ¯\(ツ)/¯
Другой (гораздо более безопасный) подход - экранировать все символы (а не только несколько специальных, которые мы в настоящее время знаем), используя escape-формат Unicode. \u{code}
:
function escapeRegExp(text) {
return Array.from(text)
.map(char => `\\u{${char.charCodeAt(0).toString(16)}}`)
.join('');
}
console.log(escapeRegExp('a.b')); // '\u{61}\u{2e}\u{62}'
Обратите внимание, что вам необходимо пройти u
флаг для работы этого метода:
var expression = new RegExp(escapeRegExp(usersString), 'u');
Это более короткая версия.
RegExp.escape = function(s) {
return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
}
Это включает немета-символы %
, &
, '
, а также ,
, но спецификация JavaScript RegExp позволяет это.
escapeRegExp = function(str) {
if (str == null) return '';
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
XRegExp имеет функцию escape:
XRegExp.escape('Escaped? <.>');
// -> 'Escaped\?\ <\.>'
Больше на: http://xregexp.com/api/
Вместо того, чтобы экранировать только символы, которые вызовут проблемы в вашем регулярном выражении (например, черный список), почему бы не использовать вместо этого белый список. Таким образом, каждый персонаж считается испорченным, если он не совпадает.
Для этого примера предположим следующее выражение:
RegExp.escape('be || ! be');
Это белый список букв, цифр и пробелов:
RegExp.escape = function (string) {
return string.replace(/([^\w\d\s])/gi, '\\$1');
}
Возвращает:
"be \|\| \! be"
Это может скрывать символы, от которых не нужно убегать, но это не мешает вашему выражению (возможно, некоторые незначительные штрафы за время - но это стоит того для безопасности).
Я позаимствовал bobince ответвыше и создал функцию с шаблонатегами для создания
RegExp
где часть значения экранирована, а часть - нет.
регулярное выражение-экранированный.js
Для наших поклонников TypeScript ...
global.d.ts
interface RegExpConstructor {
/** Escapes a string so that it can be used as a literal within a `RegExp`. */
escape(text: string): string;
/**
* Returns a tagged template function that creates `RegExp` with its template values escaped.
*
* This can be useful when using a `RegExp` to search with user input.
*
* @param flags The flags to apply to the `RegExp`.
*
* @example
*
* function capitalizeFirstUserInputCaseInsensitiveMatch(text: string, userInput: string) {
* const [, before, match, after ] =
* RegExp.escaped('i')`^((?:(?!${userInput}).)*)(${userInput})?(.*)$`.exec(text);
*
* return `${before}${match.toUpperCase()}${after}`;
* }
*/
escaped(flags?: string): (regexStrings: TemplateStringsArray, ...escapedVals: Array<string | number>) => RegExp;
}
Когда-либо существовало и всегда будет 12 метасимволов, которые нужно экранировать,
чтобы их можно было считать буквальными.
Неважно, что делается с экранированной строкой, вставленной в
оболочку сбалансированного регулярного выражения, добавляемой, не имеет значения.
Замените строку, используя это
var escaped_string = oldstring.replace( /[\\^$.|?*+()[{]/g, '\\$&' );
Функции в других ответах излишни для экранирования целых регулярных выражений (они могут быть полезны для экранирования частей регулярных выражений, которые впоследствии будут объединены в большие регулярные выражения).
Если вы избегаете целого регулярного выражения и с ним покончено, цитируете метасимволы, которые либо автономны (.
, ?
, +
, *
, ^
, $
, |
, \
) или начать что-то ((
, [
, {
) это все, что тебе нужно:
String.prototype.regexEscape = function regexEscape() {
return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
};
И да, разочаровывает тот факт, что JavaScript не имеет такой встроенной функции.
Только что опубликовал суть экранирования регулярных выражений , основанную на прокладке RegExp.escape , которая, в свою очередь, была основана на отклоненном предложении RegExp.escape . Выглядит примерно эквивалентно принятому ответу, за исключением того, что он не ускользает
Текущая суть на момент написания этого:
const syntaxChars = /[\^$\\.*+?()[\]{}|]/g
/**
* Escapes all special special regex characters in a given string
* so that it can be passed to `new RegExp(escaped, ...)` to match all given
* characters literally.
*
* inspired by https://github.com/es-shims/regexp.escape/blob/master/implementation.js
*
* @param {string} s
*/
export function escape(s) {
return s.replace(syntaxChars, '\\$&')
}
Это постоянное решение.
function regExpEscapeFuture(literal_string) {
return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}
Вот версия replaceAll, не основанная на регулярных выражениях, которая работает на 40% быстрее, чем аналогичная версия на основе регулярных выражений.
Использует функцию string.includes и троичный код для добавления escape-обратной косой черты вместо регулярного выражения.