Corresponder texto de múltiplas linhas usando expressão regular

Eu estou tentando combinar um texto de várias linhas usando java. Quando eu uso a class Pattern com o modificador Pattern.MULTILINE , eu sou capaz de combinar, mas não consigo fazer isso com (?m).

O mesmo padrão com (?m) e usando String.matches não parece funcionar.

Tenho certeza de que estou sentindo falta de algo, mas não sei o que. Não sou muito bom em expressões regulares.

Isso é o que eu tentei

 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? 

Primeiro, você está usando os modificadores sob uma suposição incorreta.

Pattern.MULTILINE ou (?m) informa ao Java para aceitar as âncoras ^ e $ para combinar no início e no final de cada linha (caso contrário, elas correspondem apenas ao início / fim de toda a cadeia).

Pattern.DOTALL ou (?s) informa ao Java para permitir que o ponto corresponda também aos caracteres de nova linha.

Segundo, no seu caso, o regex falha porque você está usando o método matches() que espera que o regex corresponda à string inteira – o que obviamente não funciona, já que alguns caracteres são deixados depois (\\W)*(\\S)* combinaram.

Então, se você está simplesmente procurando por uma string que comece com User Comments: :, use o regex

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

com a opção 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 conterá o texto após os User Comments:

Isso não tem nada a ver com a bandeira MULTILINE; o que você está vendo é a diferença entre os methods find() e matches() . find() é bem-sucedido se uma correspondência puder ser encontrada em qualquer lugar da string de destino , enquanto matches() espera que a regex corresponda à string inteira .

 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 

Além disso, MULTILINE não significa o que você acha que faz. Muitas pessoas parecem chegar à conclusão de que você precisa usar esse sinalizador se sua sequência de destino contiver novas linhas – ou seja, se ela contiver várias linhas lógicas. Eu vi várias respostas aqui no SO para esse efeito, mas na verdade, tudo o que a bandeira faz é mudar o comportamento das âncoras, ^ e $ .

Normalmente ^ corresponde ao início da string de destino, e $ corresponde ao final (ou antes de uma nova linha no final, mas deixaremos isso de lado por enquanto). Mas se a string contiver novas linhas, você pode escolher ^ e $ para combinar no início e no final de qualquer linha lógica, não apenas no início e no final da string inteira, configurando o flag MULTILINE.

Então, esqueça o que significa MULTILINE e apenas lembre-se do que ele faz : muda o comportamento das âncoras e das âncoras. DOTALL modo DOTALL foi originalmente chamado de “linha única” (e ainda está em alguns tipos, incluindo Perl e .NET), e sempre causou confusão semelhante. Temos a sorte de os desenvolvedores de Java usarem o nome mais descritivo nesse caso, mas não havia alternativa razoável para o modo “multilinha”.

Em Perl, onde toda essa loucura começou, eles admitiram seu erro e se livraram dos modos “multilinha” e “linha única” em regexes do Perl 6. Em outros vinte anos, talvez o resto do mundo tenha seguido o exemplo.

str.matches(regex) se comporta como Pattern.matches(regex, str) que tenta corresponder toda a sequência de input ao padrão e retorna

true if, e somente se, toda a sequência de input corresponder ao padrão desse correspondente

Considerando que matcher.find() tenta encontrar a próxima subsequência da seqüência de input que corresponde ao padrão e retorna

true if, e somente se, uma subsequência da seqüência de input corresponder ao padrão desse correspondente

Assim, o problema está na regex. Experimente o seguinte.

 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 

Assim, em suma, a porção (\\W)*(\\S)* na sua primeira regex corresponde a uma cadeia vazia como * significa zero ou mais ocorrências e a cadeia real correspondida é User Comments: e não a cadeia inteira como você d esperar. O segundo falha ao tentar corresponder a string inteira, mas não pode, pois \\W combina um caractere que não seja uma palavra, isto é, [^a-zA-Z0-9_] e o primeiro caractere é T , um caractere de palavra.