Existe uma função RegExp.escape em Javascript?

442

Eu só quero criar uma expressão regular a partir de qualquer string possível.

var usersString = "Hello?!*`~World()[]";
var expression = new RegExp(RegExp.escape(usersString))
var matches = "Hello".match(expression);

Existe um método embutido para isso? Se não, o que as pessoas usam? Ruby tem RegExp.escape. Eu não sinto que precisaria escrever o meu próprio, tem que haver algo padrão por aí. Obrigado!

Lance Pollard
fonte
15
Só queria atualizá-lo sobre as pessoas que RegExp.escapeestão trabalhando atualmente e qualquer pessoa que pense ter uma contribuição valiosa é muito bem-vinda para contribuir. core-js e outros polyfills oferecem isso.
Benjamin Gruenbaum
5
De acordo com a recente atualização desta resposta, esta proposta foi rejeitada: Veja o problema
try-catch-finalmente

Respostas:

573

A função vinculada acima é insuficiente. Falha ao escapar ^ou $(início e fim da string), ou -, que em um grupo de caracteres é usado para intervalos.

Use esta função:

function escapeRegex(string) {
    return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

Embora possa parecer desnecessário à primeira vista, o escape -(e também ^) torna a função adequada para caracteres de escape a serem inseridos em uma classe de caracteres, bem como no corpo do regex.

Escapando / escape torna a função adequada para caracteres de escape a serem usados ​​em um literal de regex JS para avaliação posterior.

Como não há desvantagem em escapar de um deles, faz sentido escapar para cobrir casos de uso mais amplos.

E sim, é uma falha decepcionante que isso não faça parte do JavaScript padrão.

bobince
fonte
16
na verdade, não precisa escapar /em tudo
Thorn
28
@Paul: Perl quotemeta( \Q), Python re.escape, PHPpreg_quote , Ruby Regexp.quote...
bobince
13
Se você usar essa função em um loop, provavelmente é melhor tornar o objeto RegExp sua própria variável var e = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;e, em seguida, sua função é return s.replace(e, '\\$&');Dessa forma, você só instancia o RegExp uma vez.
styfle
15
Os argumentos padrão contra o aumento de objetos internos se aplicam aqui, não? O que acontece se uma versão futura do ECMAScript fornecer uma RegExp.escapecuja implementação seja diferente da sua? Não seria melhor que essa função não fosse anexada a nada?
Mark Amery
15
bobince não se importa com a opinião de eslint
bobince
113

Para quem usa o lodash, desde a v3.0.0, uma função _.escapeRegExp está integrada:

_.escapeRegExp('[lodash](https://lodash.com/)');
// → '\[lodash\]\(https:\/\/lodash\.com\/\)'

E, caso você não queira exigir a biblioteca completa do lodash, pode ser necessário apenas essa função !

gustavohenke
fonte
6
há até um pacote npm disso! npmjs.com/package/lodash.escaperegexp
Ted Pennings
1
Isso importa um monte de código que realmente não precisa estar lá para uma coisa tão simples. Use a resposta de bobince ... funciona para mim e tem muitos menos bytes para carregar do que a versão lodash!
Rob Evans
6
@RobEvans, minha resposta começa com "Para quem usa o lodash" e até mencionei que você pode exigir apenas a escapeRegExpfunção.
precisa saber é o seguinte
2
@gustavohenke Desculpe, eu deveria ter sido um pouco mais claro, incluí o módulo vinculado à sua "apenas essa função" e é isso que eu estava comentando. Se você der uma olhada, é bastante código para o que efetivamente deveria ser uma única função com um único regexp. Concorde se você já estiver usando o lodash, então faz sentido usá-lo, mas use a outra resposta. Desculpe pelo comentário pouco claro.
Rob Evans
2
@maddob eu não posso ver que \ x3 você mencionou: minhas cordas escaparam estão boas, apenas o que eu esperava
Federico Fissore
43

A maioria das expressões aqui resolve casos de uso específicos únicos.

Tudo bem, mas eu prefiro uma abordagem "sempre funciona".

function regExpEscape(literal_string) {
    return literal_string.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, '\\$&');
}

Isso "escapará totalmente" de uma cadeia literal para qualquer um dos seguintes usos em expressões regulares:

  • Inserção em uma expressão regular. Por exemplonew RegExp(regExpEscape(str))
  • Inserção em uma classe de caractere. Por exemplonew RegExp('[' + regExpEscape(str) + ']')
  • Inserção no especificador de número inteiro. Por exemplonew RegExp('x{1,' + regExpEscape(str) + '}')
  • Execução em mecanismos de expressão regular não JavaScript.

Caracteres Especiais Cobertos:

  • -: Cria um intervalo de caracteres em uma classe de caracteres.
  • [/ ]: Inicia / termina uma classe de personagem.
  • {/ }: Inicia / termina um especificador de numeração.
  • (/ ): Inicia / termina um grupo.
  • */ +/ ?: Especifica o tipo de repetição.
  • .: Corresponde a qualquer caractere.
  • \: Ignora caracteres e inicia entidades.
  • ^: Especifica o início da zona correspondente e nega a correspondência em uma classe de caracteres.
  • $: Especifica o fim da zona correspondente.
  • |: Especifica alternância.
  • #: Especifica o comentário no modo de espaçamento livre.
  • \s: Ignorado no modo de espaçamento livre.
  • ,: Separa valores no especificador de numeração.
  • /: Inicia ou termina a expressão.
  • :: Conclui tipos de grupos especiais e parte de classes de caracteres no estilo Perl.
  • !: Nega o grupo de largura zero.
  • </ =: Parte das especificações do grupo de largura zero.

Notas:

  • /não é estritamente necessário em qualquer sabor de expressão regular. No entanto, ele protege no caso de alguém (tremor) faz eval("/" + pattern + "/");.
  • , garante que, se a string for um número inteiro no especificador numérico, ela causará corretamente um erro de compilação RegExp em vez de compilar incorretamente silenciosamente.
  • #, e \snão precisa ser escapado em JavaScript, mas sim em muitos outros tipos. Eles são escapados aqui caso a expressão regular seja passada posteriormente para outro programa.

Se você também precisa proteger a expressão regular contra possíveis adições aos recursos do mecanismo de expressão regular JavaScript, recomendo usar o mais paranóico:

function regExpEscapeFuture(literal_string) {
    return literal_string.replace(/[^A-Za-z0-9_]/g, '\\$&');
}

Essa função escapa de todos os caracteres, exceto aqueles explicitamente garantidos, que não serão utilizados para sintaxe em futuros sabores de expressões regulares.


Para quem realmente gosta de saneamento, considere este exemplo:

var s = '';
new RegExp('(choice1|choice2|' + regExpEscape(s) + ')');

Isso deve compilar bem em JavaScript, mas não em outros tipos. Se pretender passar para outro sabor, o caso nulo de s === ''deve ser verificado independentemente, da seguinte forma:

var s = '';
new RegExp('(choice1|choice2' + (s ? '|' + regExpEscape(s) : '') + ')');
Pi Marillion
fonte
1
O /não precisa ser escapado na [...]classe de personagem.
Dan Dascalescu
1
A maioria deles não precisa ser escapada. "Cria um intervalo de caracteres em uma classe de caracteres" - você nunca está em uma classe de caracteres dentro da string. "Especifica o comentário no modo de espaçamento livre, ignorado no modo de espaçamento livre" - não suportado em javascript. "Separa valores no especificador de numeração" - você nunca está no especificador de numerário dentro da string. Além disso, você não pode escrever texto arbitrário dentro da especificação de nomeação. "Inicia ou termina a expressão" - não é necessário escapar. Eval não é um caso, pois exigiria muito mais fuga. [será continuado no próximo comentário]
Qwertiy
"Conclui tipos de grupos especiais e parte das classes de caracteres no estilo Perl" - parece não estar disponível em javascript. "Nega grupo de largura zero, parte das especificações de grupo com largura zero" - você nunca tem grupos dentro da string.
precisa saber é o seguinte
@ Qwertiy A razão para essas fugas extras é eliminar casos extremos que podem causar problemas em certos casos de uso. Por exemplo, o usuário dessa função pode querer inserir a cadeia de caracteres regex de escape em outra regex como parte de um grupo ou mesmo para uso em outro idioma além do Javascript. A função não faz suposições como "Eu nunca farei parte de uma classe de personagem", porque ela deve ser geral . Para uma abordagem mais YAGNI, consulte qualquer uma das outras respostas aqui.
Marillion Pi 22/09
Muito bom. Por que _ não escapou? O que garante que provavelmente não se tornará a sintaxe regex posteriormente?
madprops
30

O Guia de Expressões Regulares da Rede de Desenvolvedores da Mozilla fornece esta função de escape:

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
quietmint
fonte
@DanDascalescu Você está certo. A página MDN foi atualizada e =não está mais incluída.
quietmint
21

No widget de preenchimento automático do jQueryUI (versão 1.9.1), eles usam um regex ligeiramente diferente (Linha 6753), eis a expressão regular combinada com a abordagem @bobince.

RegExp.escape = function( value ) {
     return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
Pierluc SS
fonte
4
A única diferença é que eles escapam ,(que não é um metacaractere) #e espaços em branco que são importantes apenas no modo de espaço livre (que não é suportado pelo JavaScript). No entanto, eles acertam em não escapar da barra.
Martin Ender
18
Se você deseja reutilizar a implementação da interface do usuário do jquery, em vez de colar o código localmente, vá em $.ui.autocomplete.escapeRegex(myString).
23913 Scott Stafford
2
lodash também tem isso, _. escapeRegExp e npmjs.com/package/lodash.escaperegexp
Ted Pennings
v1.12 o mesmo, ok!
Peter Krauss
13

Nada deve impedir você de escapar de todos os caracteres não alfanuméricos:

usersString.replace(/(?=\W)/g, '\\');

Você perde um certo grau de legibilidade ao fazê- re.toString()lo, mas ganha muita simplicidade (e segurança).

De acordo com ECMA-262, por um lado, de expressão "caracteres de sintaxe" regulares são sempre não-alfanumérico, de tal forma que o resultado é seguro e sequências de escape especiais ( \d, \w, \n) estão sempre alfanumérico, que será produzido não escapa de controle falsos .

filip
fonte
Simples e eficaz. Eu gosto disso muito melhor do que a resposta aceita. Para navegadores (realmente) antigos, .replace(/[^\w]/g, '\\$&')funcionaria da mesma maneira.
Tomas Langkaas
6
Isso falha no modo Unicode. Por exemplo, new RegExp('🍎'.replace(/(?=\W)/g, '\\'), 'u')lança exceção porque \Wcorresponde a cada unidade de código de um par substituto separadamente, resultando em códigos de escape inválidos.
Alexey Lebedev
1
alternativa:.replace(/\W/g, "\\$&");
Miguel Pynto 21/03
@AlexeyLebedev A resposta foi corrigida para lidar com o modo Unicode? Ou existe uma solução em outro lugar que mantém, mantendo essa simplicidade?
johny why
6

Esta é uma versão mais curta.

RegExp.escape = function(s) {
    return s.replace(/[$-\/?[-^{|}]/g, '\\$&');
}

Isso inclui os não-caracteres meta de %, &, ', e ,, mas a especificação JavaScript RegExp permite isso.

kzh
fonte
2
Eu não usaria essa versão "mais curta", pois os intervalos de caracteres ocultam a lista de caracteres, o que torna mais difícil verificar a correção à primeira vista.
nhahtdh
@nhahtdh Eu provavelmente também não, mas é postado aqui para obter informações.
kzh
@ kzh: postar "para obter informações" ajuda menos do que postar para entender. Você não concorda que minha resposta é mais clara?
Dan Dascalescu
Pelo menos, .está faltando. E (). Ou não? [-^é estranho. Não me lembro do que está lá.
precisa saber é o seguinte
Esses estão no intervalo especificado.
kzh
3

Em vez de apenas escapar caracteres que causam problemas em sua expressão regular (por exemplo: uma lista negra), por que não considerar usar uma lista de permissões. Dessa forma, cada personagem é considerado contaminado, a menos que corresponda.

Para este exemplo, assuma a seguinte expressão:

RegExp.escape('be || ! be');

Esta lista de permissões inclui letras, número e espaços:

RegExp.escape = function (string) {
    return string.replace(/([^\w\d\s])/gi, '\\$1');
}

Devoluções:

"be \|\| \! be"

Isso pode escapar aos caracteres que não precisam ser escapados, mas isso não atrapalha sua expressão (talvez algumas pequenas penalidades de tempo - mas vale a pena por segurança).

bashaus
fonte
Ele é diferente da resposta do @ filip? stackoverflow.com/a/40562456/209942
johny why
3
escapeRegExp = function(str) {
  if (str == null) return '';
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
Ravi Gadhia
fonte
1

As funções nas outras respostas são um exagero para escapar de expressões regulares inteiras (elas podem ser úteis para escapar partes de expressões regulares que serão posteriormente concatenadas em regexps maiores).

Se você escapar de uma expressão regular inteira e é feito com ele, citando os metacaracteres que são ou autônomo ( ., ?, +, *, ^, $, |, \) ou começar algo ( (, [, {) é tudo que você precisa:

String.prototype.regexEscape = function regexEscape() {
  return this.replace(/[.?+*^$|({[\\]/g, '\\$&');
};

E sim, é decepcionante que o JavaScript não tenha uma função como essa embutida.

Dan Dascalescu
fonte
Digamos que você escape da entrada do usuário (text)nexte insira-a em: (?:+ input + ). Seu método fornecerá a sequência resultante (?:\(text)next)que não será compilada. Note-se que esta é uma inserção razoável, não algum um louco como re\+ entrada + re(neste caso, o programador pode ser responsabilizado por fazer algo estúpido)
nhahtdh
1
@ nhahtdh: minha resposta mencionou especificamente escapar expressões regulares inteiras e "estar sendo feito" com elas, não partes (ou partes futuras) de regexps. Desfazer o voto negativo?
Dan Dascalescu
Raramente é o caso de você escapar de toda a expressão - há operação de string, que é muito mais rápida em comparação com regex, se você deseja trabalhar com string literal.
Nhttdh 28/11
Isso não está mencionando que está incorreto - \deve ser escapado, pois seu regex ficará \wintacto. Além disso, o JavaScript parece não permitir o rastreamento ), pelo menos é para isso que o Firefox lança erros.
nhahtdh
1
Por favor, abordar a parte sobre o fechamento)
nhahtdh
1

Outra abordagem (muito mais segura) é escapar de todos os caracteres (e não apenas alguns especiais que atualmente conhecemos) usando o formato de 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}'

Observe que você precisa passar a ubandeira para que este método funcione:

var expression = new RegExp(escapeRegExp(usersString), 'u');
soheilpro
fonte
1

Só houve e sempre haverá 12 meta caracteres que precisam ser escapados
para serem considerados literais.

Não importa o que é feito com a cadeia de caracteres de escape, inserida em um
wrapper regex balanceado , anexado, não importa.

Substitua uma string usando este

var escaped_string = oldstring.replace( /[\\^$.|?*+()[{]/g, '\\$&' );

fonte
que tal ]?
Thomasleveil 5/10/19