Coincidencia de texto de líneas múltiples con expresión regular

Estoy tratando de hacer coincidir un texto de varias líneas usando java. Cuando uso la clase Pattern con el modificador Pattern.MULTILINE , puedo hacer coincidir, pero no puedo hacerlo con (?m).

El mismo patrón con (?m) y el uso de String.matches no parece funcionar.

Estoy seguro de que me falta algo, pero no tengo idea de qué. No soy muy bueno en expresiones regulares.

Esto es lo que probé

 String test = "User Comments: This is \ta\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? 

Primero, estás usando los modificadores bajo una suposición incorrecta.

Pattern.MULTILINE o (?m) le dice a Java que acepte los anclajes ^ y $ para coincidir al comienzo y al final de cada línea (de lo contrario, solo coinciden al principio / final de la cadena completa).

Pattern.DOTALL o (?s) le dice a Java que permita que el punto coincida con los caracteres de nueva línea.

En segundo lugar, en su caso, la expresión regular falla porque está utilizando el método matches() que espera que la expresión regular coincida con toda la cadena, lo que por supuesto no funciona, ya que quedan algunos caracteres después de (\\W)*(\\S)* han coincidido.

Entonces, si simplemente está buscando una cadena que comience con User Comments: use la expresión regular

 ^\s*User Comments:\s*(.*) 

con la opción Pattern.DOTALL :

 Pattern regex = Pattern.compile("^\\s*User Comments:\\s+(.*)", Pattern.DOTALL); Matcher regexMatcher = regex.matcher(subjectString); if (regexMatcher.find()) { ResultString = regexMatcher.group(1); } 

ResultString contendrá el texto después User Comments:

Esto no tiene nada que ver con la bandera MULTILINE; lo que está viendo es la diferencia entre los métodos find() y matches() . find() tiene éxito si se puede encontrar una coincidencia en cualquier parte de la cadena objective , mientras que matches() espera que la expresión regular coincida con la cadena completa .

 Pattern p = Pattern.compile("xyz"); Matcher m = p.matcher("123xyzabc"); System.out.println(m.find()); // true System.out.println(m.matches()); // false Matcher m = p.matcher("xyz"); System.out.println(m.matches()); // true 

Además, MULTILINE no significa lo que crees que hace. Muchas personas parecen saltar a la conclusión de que tiene que usar esa bandera si su cadena objective contiene líneas nuevas, es decir, si contiene múltiples líneas lógicas. He visto varias respuestas aquí en SO para ese efecto, pero de hecho, todo lo que hace la bandera es cambiar el comportamiento de las anclas, ^ y $ .

Normalmente ^ coincide con el comienzo de la cadena objective, y $ coincide con el final (o antes de una nueva línea al final, pero lo dejaremos de lado por el momento). Pero si la cadena contiene nuevas líneas, puede elegir que ^ y $ coincidan al principio y al final de cualquier línea lógica, no solo al comienzo y al final de toda la cadena, configurando el indicador MULTILINE.

Así que olvídate de lo que significa MULTILINE y simplemente recuerda lo que hace : cambia el comportamiento de los anclajes ^ y $ . DOTALL modo DOTALL originalmente se llamaba “línea única” (y todavía está en algunos sabores, incluidos Perl y .NET), y siempre ha causado una confusión similar. Somos afortunados de que los desarrolladores de Java usaran 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 Perl 6 expresiones regulares. En otros veinte años, tal vez el rest del mundo habrá seguido su ejemplo.

str.matches(regex) comporta como Pattern.matches(regex, str) que intenta hacer coincidir toda la secuencia de entrada con el patrón y devuelve

true si, y solo si, toda la secuencia de entrada coincide con el patrón de este matcher

Mientras que matcher.find() intenta encontrar la siguiente subsecuencia de la secuencia de entrada que coincide con el patrón y regresa

true si, y solo si, una subsecuencia de la secuencia de entrada coincide con el patrón de este matcher

Por lo tanto, el problema es con la expresión regular. Prueba lo siguiente.

 String test = "User Comments: This is \ta\ta \ntest\n\n message \n"; String pattern1 = "User Comments: [\\s\\S]*^test$[\\s\\S]*"; Pattern p = Pattern.compile(pattern1, Pattern.MULTILINE); System.out.println(p.matcher(test).find()); //true String pattern2 = "(?m)User Comments: [\\s\\S]*^test$[\\s\\S]*"; System.out.println(test.matches(pattern2)); //true 

Así, en resumen, la porción (\\W)*(\\S)* en su primera expresión regular coincide con una cadena vacía ya que * significa cero o más ocurrencias y la cadena real coincidente es User Comments: y no toda la cadena como usted d esperar El segundo falla, ya que trata de hacer coincidir toda la cadena, pero no puede, ya que \\W coincide con un carácter que no es palabra, es decir [^a-zA-Z0-9_] y el primer carácter es T , un carácter de palabra.