Guia de escape de caracteres no Java RegExps
1. Visão geral
A API de expressões regulares em Java,java.util.regex, é amplamente usada para correspondência de padrões. Para descobrir mais, você pode seguir estearticle.
Neste artigo, focaremos em caracteres de escape com uma expressão regular e mostraremos como isso pode ser feito em Java.
2. Caracteres RegExp Especiais
De acordo com a documentação da API de expressões regulares do Java, há um conjunto de caracteres especiais também conhecidos como metacaracteres presentes em uma expressão regular.
Quando queremos permitir os personagens como estão, em vez de interpretá-los com seus significados especiais, precisamos escapar deles. Ao escapar desses caracteres, forçamos que sejam tratados como caracteres comuns ao corresponder uma string a uma determinada expressão regular.
Os metacaracteres dos quais geralmente precisamos escapar dessa maneira são:
<([\{\^-=$!|]})? +.> *
Vejamos um exemplo de código simples onde combinamos uma entradaString com um padrão expresso em uma expressão regular.
Este teste mostra que, para uma determinada string de entradafoof, quando o padrãofoo. (foo terminando com um caractere de ponto) for correspondido, ele retorna um valor detrue que indica que a correspondência foi bem-sucedida.
@Test
public void givenRegexWithDot_whenMatchingStr_thenMatches() {
String strInput = "foof";
String strRegex = "foo.";
assertEquals(true, strInput.matches(strRegex));
}
Você pode se perguntar por que a correspondência é bem-sucedida quando não há caractere de ponto (.) Presente na entradaString?
A resposta é simples. O ponto (.) É um metacaractere - o significado especial do ponto aqui é que pode haver "qualquer caractere" em seu lugar. Portanto, é claro como o combinador determinou que uma correspondência foi encontrada.
Digamos que não queremos tratar o caractere ponto (.) Com seu significado único. Em vez disso, queremos que seja interpretado como um sinal de ponto. Isso significa que no exemplo anterior, não queremos permitir que o padrãofoo. tenha uma correspondência na entradaString.
Como lidaríamos com uma situação como essa? A resposta é:we need to escape the dot (.) character so that its special meaning gets ignored.
Vamos investigá-lo com mais detalhes na próxima seção.
3. Escapando Personagens
De acordo com a documentação da API Java para expressões regulares, há duas maneiras pelas quais podemos escapar de caracteres que têm um significado especial. Em outras palavras, forçá-los a serem tratados como caracteres comuns.
Vamos ver o que são:
-
Preceder um metacaractere com uma barra invertida (\)
-
Envolva um metacaractere com\Qe\E
Isso significa apenas que, no exemplo que vimos anteriormente, se quisermos escapar do caractere de ponto, precisamos colocar um caractere de barra invertida antes do caractere de ponto. Como alternativa, podemos colocar o caractere de ponto entre \ Q e \ E.
3.1. Escapando usando barra invertida
Essa é uma das técnicas que podemos usar para escapar dos metacaracteres em uma expressão regular. No entanto, sabemos que o caractere de barra invertida também é um caractere de escape nos literais JavaString. Portanto, precisamos dobrar o caractere de barra invertida ao usá-lo para preceder qualquer caractere (incluindo o próprio caractere \).
Portanto, em nosso exemplo, precisamos alterar a expressão regular, como mostrado neste teste:
@Test
public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() {
String strInput = "foof";
String strRegex = "foo\\.";
assertEquals(false, strInput.matches(strRegex));
}
Aqui, o caractere de ponto é escapado; portanto, o correspondente o trata como um ponto e tenta encontrar um padrão que termina com o ponto (ou seja, foo.).
Nesse caso, ele retornafalse, pois não há correspondência na entradaString para esse padrão.
3.2. Escapando usando \ Q & \ E
Como alternativa, podemos usar\Qe\E para escapar do caractere especial. \Q indica que todos os caracteres até\E precisam de escape e\E significa que precisamos terminar o escape que foi iniciado com\Q.
Isso significa apenas que o que quer que esteja entre\Qe\E teria escape.
No teste mostrado aqui, osplit() da classeString faz uma correspondência usando a expressão regular fornecida a ela.
Nosso requisito é dividir a sequência de entrada pelo caractere de barra vertical (|) em palavras. Portanto, usamos um padrão de expressão regular para fazer isso.
O caractere de pipe é um metacaractere que precisa ser escapado na expressão regular.
Aqui, o escape é feito colocando a barra vertical entre\Qe\E:
@Test
public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() {
String strInput = "foo|bar|hello|world";
String strRegex = "\\Q|\\E";
assertEquals(4, strInput.split(strRegex).length);
}
4. O método Pattern.Quote (String S)
O método Pattern.Quote (String S) na classejava.util.regex.Pattern converte um determinado padrão de expressão regularString em um padrão literalString. Isso significa que todos os metacaracteres na entradaString são tratados como personagens comuns.
Usar este método seria uma alternativa mais conveniente do que usar\Q &\E, pois envolve oString fornecido com eles.
Vamos ver este método em ação:
@Test
public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() {
String strInput = "foo|bar|hello|world";
String strRegex = "|";
assertEquals(4,strInput.split(Pattern.quote(strRegex)).length);
}
Neste teste rápido, o métodoPattern.quote() é usado para escapar do padrão regex fornecido e transformá-lo em um literalString. Em outras palavras, ele escapa a todos os metacaracteres presentes no padrão regex para nós. Ele está fazendo um trabalho semelhante a\Q &\E.
O caractere de barra vertical é escapado pelo métodoPattern.quote()esplit() o interpreta como um literalString pelo qual divide a entrada.
Como podemos ver, essa é uma abordagem muito mais limpa e os desenvolvedores também não precisam se lembrar de todas as seqüências de escape.
5. Exemplos Adicionais
Vejamos como funciona o métodoreplaceAll() dejava.util.regex.Matcher.
Se precisarmos substituir todas as ocorrências de um determinado caractereString por outro, podemos usar esse método passando uma expressão regular para ele.
Imagine que temos uma entrada com várias ocorrências do caractere$. O resultado que queremos obter é a mesma string com o caractere$ substituído por £.
Este teste demonstra como o padrão$ é passado sem ser escapado:
@Test
public void givenRegexWithDollar_whenReplacing_thenNotReplace() {
String strInput = "I gave $50 to my brother."
+ "He bought candy for $35. Now he has $15 left.";
String strRegex = "$";
String strReplacement = "£";
String output = "I gave £50 to my brother."
+ "He bought candy for £35. Now he has £15 left.";
Pattern p = Pattern.compile(strRegex);
Matcher m = p.matcher(strInput);
assertThat(output, not(equalTo(m.replaceAll(strReplacement))));
}
O teste afirma que$ não é substituído corretamente por£.
Agora, se escaparmos do padrão regex, a substituição ocorrerá corretamente e o teste passará conforme mostrado neste trecho de código:
@Test
public void givenRegexWithDollarEsc_whenReplacing_thenReplace() {
String strInput = "I gave $50 to my brother."
+ "He bought candy for $35. Now he has $15 left.";
String strRegex = "\\$";
String strReplacement = "£";
String output = "I gave £50 to my brother."
+ "He bought candy for £35. Now he has £15 left.";
Pattern p = Pattern.compile(strRegex);
Matcher m = p.matcher(strInput);
assertEquals(output,m.replaceAll(strReplacement));
}
Observe o\$ aqui, que faz o truque escapando do caractere$ e combinando com sucesso o padrão.
6. Conclusão
Neste artigo, vimos caracteres de escape em expressões regulares em Java.
Discutimos por que as expressões regulares precisam ser escapadas e as diferentes maneiras pelas quais isso pode ser alcançado.
Como sempre, o código-fonte relacionado a este artigo pode ser encontradoover on GitHub.