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

Другие вопросы по тегам