Estoy tratando de hacer coincidir un texto de varias líneas con Java. Cuando uso elPattern
clase con el Pattern.MULTILINE
modificador, puedo hacer coincidir, pero no puedo hacerlo con(?m).
El mismo patrón con (?m)
y usando String.matches
no parece funcionar.
Estoy seguro de que me falta algo, pero no tengo idea de qué. No soy muy bueno con las expresiones regulares.
Esto es lo que probé
String test = "User Comments: This is \t a\ta \n test \n\n message \n";
String pattern1 = "User Comments: (\\W)*(\\S)*";
Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE);
System.out.println(p.matcher(test).find()); //true
String pattern2 = "(?m)User Comments: (\\W)*(\\S)*";
System.out.println(test.matches(pattern2)); //false - why?
(?s)User Comments:\s*(.*)
. De la respuesta de @Amarghosh obtuve el patrónUser Comments: [\\s\\S]*
. Entre estos, ¿hay una forma mejor o recomendada o son solo dos formas diferentes de hacer lo mismo?[\s\S]
es un poco más explícita ( "coincidirá con cualquier carácter que puede ser un espacio en blanco o no está en blanco"),.
es más fácil de leer, pero hay que buscar la(?s)
oDOTALL
modificador con el fin de averiguar si los saltos de línea se incluyen o no. Preferiría.
con elPattern.DOTALL
conjunto de banderas (esto es más fácil de leer y recordar que(?s)
en mi opinión. Debería usar lo que le.*
conDOTALL
es más legible. Utilicé el otro para mostrar que el problema está en las diferencias entre str.matches y matcher.find y no en las banderas. +1.*
conPattern.DOTALL
, pero tendré que ir con (? S) porque tengo que usarString.matches
.Esto no tiene nada que ver con la bandera MULTILINE; Lo que estás viendo es la diferencia entre los métodos
find()
ymatches()
.find()
tiene éxito si se puede encontrar una coincidencia en cualquier parte de la cadena de destino , mientras sematches()
espera que la expresión regular coincida con la cadena completa .Además,
MULTILINE
no significa lo que crees que hace. Muchas personas parecen llegar a la conclusión de que debe usar esa bandera si su cadena de destino contiene nuevas líneas, es decir, si contiene varias líneas lógicas. He visto varias respuestas aquí sobre SO para ese efecto, pero de hecho, todo lo que hace la bandera es cambiar el comportamiento de los anclajes,^
y$
.Normalmente
^
coincide con el comienzo de la cadena de destino y$
coincide con el final (o antes de una nueva línea al final, pero lo dejaremos de lado por ahora). Pero si la cadena contiene nuevas líneas, puede elegir^
y$
coincidir al comienzo y al final de cualquier línea lógica, no solo al inicio y al final de toda la cadena, configurando el indicador MULTILINE.Así que olvídate de lo que
MULTILINE
significa y sólo recuerda lo que hace : cambia el comportamiento de la^
y$
anclajes.DOTALL
Originalmente, el modo se llamaba "una sola línea" (y todavía tiene algunos sabores, incluidos Perl y .NET), y siempre ha causado una confusión similar. Somos afortunados de que los desarrolladores de Java hayan elegido el nombre más descriptivo en ese caso, pero no había una alternativa razonable para el modo "multilínea".En Perl, donde comenzó toda esta locura, admitieron su error y se deshicieron de los modos "multilínea" y "línea única" en las expresiones regulares de Perl 6. En otros veinte años, tal vez el resto del mundo habrá seguido su ejemplo.
fuente
str.matches(regex)
se comporta como elPattern.matches(regex, str)
que intenta hacer coincidir toda la secuencia de entrada con el patrón y devuelveMientras que
matcher.find()
intenta encontrar la siguiente subsecuencia de la secuencia de entrada que coincida con el patrón y devuelvePor lo tanto, el problema es con la expresión regular. Intenta lo siguiente.
En resumen, la
(\\W)*(\\S)*
porción en su primera expresión regular coincide con una cadena vacía, lo que*
significa cero o más ocurrencias y la cadena real coincidente esUser Comments:
y no la cadena completa como cabría esperar. El segundo falla cuando intenta hacer coincidir toda la cadena, pero no puede\\W
coincidir con un carácter que no sea de palabra, es decir,[^a-zA-Z0-9_]
y el primer carácter esT
un carácter de palabra.fuente
User Comments: [\\s\\S]*
y esto funcionó. (¡gracias!) De la respuesta de @Tim obtuve el patrónUser Comments:(.*)
, esto también está bien. Ahora, ¿hay alguna forma recomendada o mejor entre estas, o estas son solo dos formas de hacer lo mismo?(.*)
junto con laDOTALL
bandera es más obvio / legible que([\\s\\S]*)
La bandera multilínea le dice a regex que haga coincidir el patrón con cada línea en lugar de la cadena completa para sus propósitos, un comodín será suficiente.
fuente