1. Información general
La API de expresiones regulares en Java, java.util.regex , se usa ampliamente para la coincidencia de patrones. Para descubrir más, puede seguir este artículo.
En este artículo, nos centraremos en el escape de caracteres dentro de una expresión regular y mostraremos cómo se puede hacer en Java.
2. Caracteres RegExp especiales
De acuerdo con la documentación de la API de expresiones regulares de Java, hay un conjunto de caracteres especiales también conocidos como metacaracteres presentes en una expresión regular.
Cuando queremos permitir los caracteres como están en lugar de interpretarlos con sus significados especiales, debemos escapar de ellos. Al escapar de estos caracteres, los obligamos a ser tratados como caracteres ordinarios al hacer coincidir una cadena con una expresión regular determinada.
Los metacaracteres de los que normalmente necesitamos escapar de esta forma son:
Veamos un ejemplo de código simple donde hacemos coincidir una cadena de entrada con un patrón expresado en una expresión regular.
Esta prueba muestra que para una cadena de entrada dada foof cuando el patrón foo . ( foo que termina con un carácter de punto) coincide, devuelve un valor de verdadero que indica que la coincidencia se realizó correctamente.
@Test public void givenRegexWithDot_whenMatchingStr_thenMatches() { String strInput = "foof"; String strRegex = "foo."; assertEquals(true, strInput.matches(strRegex)); }
Quizás se pregunte por qué la coincidencia es exitosa cuando no hay ningún carácter de punto (.) Presente en la Cadena de entrada .
La respuesta es simple. El punto (.) Es un metacarácter; el significado especial del punto aquí es que puede haber "cualquier carácter" en su lugar. Por lo tanto, está claro cómo determinó el comparador que se encuentra una coincidencia.
Digamos que no queremos tratar el carácter de punto (.) Con su significado único. En cambio, queremos que se interprete como un signo de punto. Esto significa que en el ejemplo anterior, no queremos dejar que el patrón se mueva. para tener una coincidencia en la cadena de entrada .
¿Cómo manejaríamos una situación como esta? La respuesta es: necesitamos escapar del carácter de punto (.) Para que se ignore su significado especial.
Analicemos esto con más detalle en la siguiente sección.
3. Personajes que escapan
De acuerdo con la documentación de la API de Java para expresiones regulares, hay dos formas en las que podemos escapar de los caracteres que tienen un significado especial. En otras palabras, obligarlos a ser tratados como personajes ordinarios.
Veamos cuáles son:
- Preceder a un metacarácter con una barra invertida (\)
- Incluya un metacarácter con \ Q y \ E
Esto solo significa que en el ejemplo que vimos anteriormente, si queremos escapar del carácter de punto, necesitamos poner un carácter de barra invertida antes del carácter de punto. Alternativamente, podemos colocar el carácter de punto entre \ Q y \ E.
3.1. Escapar con barra invertida
Esta es una de las técnicas que podemos utilizar para escapar de los metacaracteres en una expresión regular. Sin embargo, sabemos que el carácter de barra invertida también es un carácter de escape en los literales Java String . Por lo tanto, necesitamos duplicar el carácter de barra invertida cuando se usa para preceder a cualquier carácter (incluido el carácter \ en sí).
Por lo tanto, en nuestro ejemplo, necesitamos cambiar la expresión regular como se muestra en esta prueba:
@Test public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() { String strInput = "foof"; String strRegex = "foo\\."; assertEquals(false, strInput.matches(strRegex)); }
Aquí, el carácter de punto se escapa, por lo que el comparador simplemente lo trata como un punto e intenta encontrar un patrón que termine con el punto (es decir, foo. ).
En este caso, devuelve falso ya que no hay ninguna coincidencia en la cadena de entrada para ese patrón.
3.2. Escapar usando \ Q & \ E
Alternativamente, podemos usar \ Q y \ E para escapar del carácter especial. \ Q indica que todos los caracteres hasta \ E necesita ser escapado y \ E significa que necesitamos para poner fin al escape que se inició con \ Q .
Esto solo significa que cualquier cosa que esté entre \ Q y \ E se escaparía.
En la prueba que se muestra aquí, el split () de la clase String hace una coincidencia usando la expresión regular que se le proporciona.
Nuestro requisito es dividir la cadena de entrada por el carácter de barra vertical (|) en palabras. Por lo tanto, usamos un patrón de expresión regular para hacerlo.
El carácter de barra vertical es un metacarácter que debe escaparse en la expresión regular.
Aquí, el escape se realiza colocando el carácter de tubería entre \ Q y \ E :
@Test public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() \\E"; assertEquals(4, strInput.split(strRegex).length);
4. El método Pattern.quote (String S)
El método Pattern.Quote (String S) de la clase java.util.regex.Pattern convierte un patrón de expresión regular determinado String en un patrón literal String. Esto significa que todos los metacaracteres de la cadena de entrada se tratan como caracteres normales.
Usar este método sería una alternativa más conveniente que usar \ Q & \ E, ya que envuelve la Cadena dada con ellos.
Veamos este método en acción:
@Test public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() bar
En esta prueba rápida, el método Pattern.quote () se usa para escapar del patrón de expresiones regulares dado y transformarlo en un literal de cadena . En otras palabras, escapa a todos los metacaracteres presentes en el patrón de expresiones regulares para nosotros. Se está haciendo un trabajo similar a \ Q y \ E .
El carácter de tubería se escapa mediante el método Pattern.quote () y split () lo interpreta como un literal de cadena por el que divide la entrada.
Como podemos ver, este es un enfoque mucho más limpio y además los desarrolladores no tienen que recordar todas las secuencias de escape.
Debemos tener en cuenta que Pattern.quote encierra todo el bloque con una única secuencia de escape. Si quisiéramos escapar de los caracteres individualmente, necesitaríamos usar un algoritmo de reemplazo de tokens.
5. Ejemplos adicionales
Veamos cómo funciona el método replaceAll () de java.util.regex.Matcher .
Si necesitamos reemplazar todas las apariciones de una Cadena de caracteres dada por otra, podemos usar este método pasándole una expresión regular.
Imagina que tenemos una entrada con múltiples apariciones del carácter $ . El resultado que queremos obtener es la misma cadena con el carácter $ reemplazado por £.
Esta prueba demuestra cómo se pasa el patrón $ sin escapar:
@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)))); }
La prueba afirma que $ no se reemplaza correctamente por £ .
Ahora, si escapamos del patrón de expresiones regulares, el reemplazo ocurre correctamente y la prueba pasa como se muestra en este fragmento 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)); }
Tenga en cuenta el \\ $ aquí, que hace el truco escapando del carácter $ y haciendo coincidir con éxito el patrón.
6. Conclusión
En este artículo, analizamos el escape de caracteres en expresiones regulares en Java.
Analizamos por qué es necesario escapar de las expresiones regulares y las diferentes formas en que se puede lograr.
Como siempre, el código fuente relacionado con este artículo se puede encontrar en GitHub.