Como escapar de texto para expressão regular em Java

320

O Java possui uma maneira integrada de escapar de texto arbitrário para que ele possa ser incluído em uma expressão regular? Por exemplo, se meus usuários digitarem "$ 5", gostaria de corresponder exatamente isso ao invés de um "5" após o final da entrada.

Matt
fonte

Respostas:

450

Desde o Java 1.5, sim :

Pattern.quote("$5");
Mike Stone
fonte
88
Por favor, não que isso não escape da string em si, mas envolva-a usando \Qe \E. Isso pode levar a resultados inesperados, por exemplo Pattern.quote("*.wav").replaceAll("*",".*"), resultará \Q.*.wav\Ee não .*\.wav, conforme o esperado.
Matthias Ronge
11
@Paramaeleon Por que você esperaria que foo (x) .bar () == x.bar ()?
Michael
7
@Paramaeleon Acho que você está entendendo mal o caso de uso.
vikingsteve
18
Eu só quero ressaltar que essa maneira de escapar se aplica também às expressões que você introduz posteriormente . Isso pode ser surpreendente. Se você fizer "mouse".toUpperCase().replaceAll("OUS","ic")isso retornará MicE. Você would't esperar que voltar MICEporque você não se aplicava toUpperCase()no ic. No meu exemplo quote()é aplicado no .*insertet replaceAll()também. Você tem que fazer outra coisa, talvez .replaceAll("*","\\E.*\\Q")funcione, mas isso é contra-intuitivo.
Matthias Ronge
2
@Paramaleon Se funcionasse adicionando escapes individuais, seu exemplo inicial ainda não faria o que você queria ... se escapasse dos caracteres individualmente, ele se tornaria *.wavo padrão regex \*\.wave o replaceAll o transformaria \.*\.wav, o que significa que seria corresponde aos arquivos cujo nome consiste em um número arbitrário de períodos seguidos por .wav. Você provavelmente precisaria replaceAll("\\*", ".*")se eles tivessem adotado a implementação mais frágil que depende do reconhecimento de todos os possíveis caracteres regex ativos e escapando deles individualmente ... isso seria muito mais fácil?
Theodore Murdock
112

Diferença entre Pattern.quotee Matcher.quoteReplacementnão estava clara para mim antes de ver o exemplo a seguir

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));
Pavel Feldman
fonte
29
Especificamente, Pattern.quotesubstitui caracteres especiais em cadeias de caracteres de pesquisa de expressões regulares, como. | + () Etc, e Matcher.quoteReplacementsubstitui caracteres especiais em cadeias de caracteres de substituição, como \ 1 para referências anteriores.
Steven
9
Eu não concordo Pattern.quote agrupa seu argumento com \ Q e \ E. Não escapa caracteres especiais.
David Medinets
5
Matcher.quoteReplacement ("4 $ &% $") produz "4 \ $ &% \ $". Escapa aos caracteres especiais.
David Medinets
4
Em outras palavras: quoteReplacementapenas se preocupa com os dois símbolos $e \ que pode, por exemplo, ser usado em cadeias de substituição como referências posteriores $1ou \1. Portanto, não deve ser usado para escapar / citar um regex.
Sebastianh
1
Impressionante. Aqui está um exemplo onde queremos substituir $Group$com T$UYO$HI. O $símbolo é especial tanto no padrão como na substituição:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
arun
29

Pode ser tarde demais para responder, mas você também pode usar Pattern.LITERAL, o que ignoraria todos os caracteres especiais durante a formatação:

Pattern.compile(textToFormat, Pattern.LITERAL);
Androidme
fonte
É especialmente bom porque você pode combiná-lo comPattern.CASE_INSENSITIVE
mjjaniec
13

Eu acho que você está procurando \Q$5\E. Veja também Pattern.quote(s)introduzido no Java5.

Consulte javadoc padrão para obter detalhes.

Rob Oxspring
fonte
Estou curioso para saber se existe alguma diferença entre isso e usar a flag LITERAL, pois o javadoc diz que não há bandeira incorporada para ativar e desativar o LITERAL: java.sun.com/j2se/1.5.0/docs/api/java/ util / regex /…
Chris Mazzola
15
Observe que literalmente usar \ Q e \ E só é bom se você souber sua entrada. Pattern.quote (s) também tratará do caso em que seu texto realmente contém essas seqüências.
Jeremy Huiskamp
10

Primeiro, se

  • você usa replaceAll ()
  • você NÃO usa Matcher.quoteReplacement ()
  • o texto a ser substituído inclui $ 1

não colocará 1 no final. Ele examinará o regex de pesquisa do primeiro grupo e sub-grupo correspondente. É isso que $ 1, $ 2 ou $ 3 significa no texto de substituição: grupos correspondentes do padrão de pesquisa.

Freqüentemente, plugue longas seqüências de texto em arquivos .properties e depois gero assuntos e corpos de email a partir deles. De fato, essa parece ser a maneira padrão de executar o i18n no Spring Framework. Coloco marcas XML, como espaços reservados, nas seqüências de caracteres e uso replaceAll () para substituir as marcas XML pelos valores em tempo de execução.

Encontrei um problema em que um usuário inseria uma figura de dólares e centavos, com um cifrão. replaceAll () engasgou, com o seguinte aparecendo em um stracktrace:

java.lang.IndexOutOfBoundsException: No group 3
at java.util.regex.Matcher.start(Matcher.java:374)
at java.util.regex.Matcher.appendReplacement(Matcher.java:748)
at java.util.regex.Matcher.replaceAll(Matcher.java:823)
at java.lang.String.replaceAll(String.java:2201)

Nesse caso, o usuário digitou "$ 3" em algum lugar da entrada e replaceAll () foi procurar no regex de pesquisa o terceiro grupo correspondente, não encontrou um e vomitou.

Dado:

// "msg" is a string from a .properties file, containing "<userInput />" among other tags
// "userInput" is a String containing the user's input

substituindo

msg = msg.replaceAll("<userInput \\/>", userInput);

com

msg = msg.replaceAll("<userInput \\/>", Matcher.quoteReplacement(userInput));

resolveu o problema. O usuário pode inserir qualquer tipo de caractere, incluindo cifrões, sem problemas. Ele se comportou exatamente da maneira que você esperaria.

Meower68
fonte
6

Para ter um padrão protegido, você pode substituir todos os símbolos por "\\\\", exceto dígitos e letras. E depois disso, você pode colocar nesse padrão protegido seus símbolos especiais para fazer com que esse padrão funcione não como um texto citado estúpido, mas realmente como um padrão, mas o seu. Sem símbolos especiais do usuário.

public class Test {
    public static void main(String[] args) {
        String str = "y z (111)";
        String p1 = "x x (111)";
        String p2 = ".* .* \\(111\\)";

        p1 = escapeRE(p1);

        p1 = p1.replace("x", ".*");

        System.out.println( p1 + "-->" + str.matches(p1) ); 
            //.*\ .*\ \(111\)-->true
        System.out.println( p2 + "-->" + str.matches(p2) ); 
            //.* .* \(111\)-->true
    }

    public static String escapeRE(String str) {
        //Pattern escaper = Pattern.compile("([^a-zA-z0-9])");
        //return escaper.matcher(str).replaceAll("\\\\$1");
        return str.replaceAll("([^a-zA-Z0-9])", "\\\\$1");
    }
}
Moscow Boy
fonte
Você não precisa escapar dos espaços. Portanto, você pode alterar seu padrão para "([^ a-zA-z0-9])".
Erel Segal-Halevi
5
Erro de digitação pequeno, grandes conseqüências: "([^ a-zA-z0-9])" também não corresponde (isto é, não escapa) [, \,], ^ do qual você certamente deseja escapar! O erro de digitação é o segundo 'z' que deve ser um 'Z', caso contrário, tudo, desde o ASCII 65 até o ASCII 122, está incluído
Zefiro
3

Pattern.quote ("blabla") funciona bem.

O Pattern.quote () funciona bem. Inclui a frase com os caracteres " \ Q " e " \ E " e, se escapar, "\ Q" e "\ E". No entanto, se você precisar fazer uma expressão regular real como escape (ou escape personalizado), poderá usar este código:

String someText = "Some/s/wText*/,**";
System.out.println(someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

Este método retorna: Some / \ s / wText * / \, **

Código por exemplo e testes:

String someText = "Some\\E/s/wText*/,**";
System.out.println("Pattern.quote: "+ Pattern.quote(someText));
System.out.println("Full escape: "+someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));
Adam111p
fonte
-2

^ O símbolo (negação) é usado para corresponder a algo que não está no grupo de caracteres.

Este é o link para Expressões regulares

Aqui estão as informações da imagem sobre negação:

Informações sobre negação

Akhil Kathi
fonte