Crie RegExps dinamicamente usando variáveis ​​de string

138

Digamos que queira tornar o seguinte reutilizável:

function replace_foo(target, replacement) {
   return target.replace("string_to_replace",replacement);
}

Eu poderia fazer algo assim:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(string_to_replace,replacement);
}

Com literais de string, isso é bastante fácil. Mas e se eu quiser ficar um pouco mais complicado com a regex? Por exemplo, digamos que eu queira substituir tudo, menos string_to_replace . Instintivamente, eu tentaria estender o acima, fazendo algo como:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(/^string_to_replace/,replacement);
}

Isso não parece funcionar. Meu palpite é que ele pensa que string_to_replaceé uma string literal, e não uma variável que representa uma string. É possível criar regexes JavaScript dinamicamente usando variáveis ​​de string? Algo assim seria ótimo se possível:

function replace_foo(target, string_to_replace, replacement) {
   var regex = "/^" + string_to_replace + "/";
   return target.replace(regex,replacement);
}
Buley
fonte

Respostas:

215

new RegExp(string, flags)onde flagsestão gou i. assim

'GODzilla'.replace( new RegExp('god', 'i'), '' )

avalia como

zilla
meder omuraliev
fonte
31
E omita os /delimitadores de regex ao usar este formulário também.
Cdhowie
111

Com literais de string, isso é fácil.

Na verdade não! O exemplo substitui apenas a primeira ocorrência de string_to_replace. Mais comumente, você deseja substituir todas as ocorrências; nesse caso, você precisa converter a sequência em um /.../gRegExp global ( ). Você pode fazer isso a partir de uma string usando o new RegExpconstrutor:

new RegExp(string_to_replace, 'g')

O problema é que qualquer caractere especial de regex na literal de cadeia se comportará de maneira especial, em vez de ser caracteres normais. Você teria que escapar com uma barra invertida para consertar isso. Infelizmente, não há uma função interna para fazer isso por você, então aqui está uma que você pode usar:

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

Observe também que quando você usa um RegExp replace(), a cadeia de substituição agora também possui um caractere especial $. Isso também deve ser evitado se você quiser ter um literal $no seu texto de substituição!

function escapeSubstitute(s) {
    return s.replace(/\$/g, '$$$$');
}

(Quatro $s porque essa é uma string de substituição - argh!)

Agora você pode implementar a substituição global de cadeias com o RegExp:

function replace_foo(target, string_to_replace, replacement) {
    var relit= escapeRegExp(string_to_replace);
    var sub= escapeSubstitute(replacement);
    var re= new RegExp(relit, 'g');
    return target.replace(re, sub);
}

Que dor. Felizmente, se tudo o que você quer fazer é substituir uma string reta sem partes adicionais do regex, existe uma maneira mais rápida:

s.split(string_to_replace).join(replacement)

...e isso é tudo. Este é um idioma comumente entendido.

digamos que quero substituir tudo, mas string_to_replace

O que isso significa, você deseja substituir todos os trechos de texto que não participam de uma correspondência contra a sequência? Uma substituição por ^certamente não faz isso, porque ^significa um token de início de cadeia, não uma negação. ^é apenas uma negação em []grupos de caracteres. Também existem lookaheads negativos (?!...), mas há problemas com isso no JScript, portanto, você geralmente deve evitá-lo.

Você pode tentar combinar 'tudo até' a string e usar uma função para descartar qualquer extensão vazia entre as strings correspondentes:

var re= new RegExp('(.*)($|'+escapeRegExp(string_to_find)+')')
return target.replace(re, function(match) {
    return match[1]===''? match[2] : replacement+match[2];
});

Aqui, novamente, uma divisão pode ser mais simples:

var parts= target.split(string_to_match);
for (var i= parts.length; i-->0;)
    if (parts[i]!=='')
        parts[i]= replacement;
return parts.join(string_to_match);
bobince
fonte
10

Como os outros disseram, use new RegExp(pattern, flags)para fazer isso. Vale a pena notar que você passará literais de string para esse construtor, portanto, toda barra invertida terá que ser escapada. Se, por exemplo, você desejasse que sua regex correspondesse a uma barra invertida, seria necessário dizer new RegExp('\\\\'), enquanto a regex literal precisaria apenas ser /\\/. Dependendo de como você pretende usar isso, você deve desconfiar de passar a entrada do usuário para essa função sem pré-processamento adequado (escapando de caracteres especiais etc.). Sem isso, seus usuários poderão obter resultados muito inesperados.

Kent
fonte
3
Essa resposta, embora não seja a mais detalhada, menciona um detalhe crucial no qual fiquei parado por uma hora: escapar de qualquer sequência especial. Por exemplo, eu estava procurando por uma palavra que começasse com um determinado termo, então o regex de que eu precisaria seria /\b[term]\B/, mas ao construí-lo, preciso ligar new RegExp("\\b"+ term + "\\B"). Pequeno, mas importante diferença, e difícil de detectar uma vez que usá-lo como um regex diretamente faz trabalho como esperado.
Byson
0

Eu acho que tenho um exemplo muito bom para destacar texto em string (ele encontra não olhando para o registro, mas destacado usando o registro)

function getHighlightedText(basicString, filterString) {

    if ((basicString === "") || (basicString === null) || (filterString === "") || (filterString === null)) return basicString;

    return basicString.replace(new RegExp(filterString.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\\\$&'), 'gi'),
        function(match)
            {return "<mark>"+match+"</mark>"});

}

http://jsfiddle.net/cdbzL/1258/

Zhurov Konstantin
fonte
0

Uma solução realmente simples para isso é esta:

function replace(target, string_to_replace, replacement) {
  return target.split(string_to_replace).join(replacement);
}

Não há necessidade de Regexes

Também parece ser o mais rápido nos navegadores modernos https://jsperf.com/replace-vs-split-join-vs-replaceall

Jack Allan
fonte