Estou tentando combinar um texto de várias linhas usando java. Quando uso a Pattern
classe com o Pattern.MULTILINE
modificador, consigo fazer a correspondência, mas não consigo fazer isso com(?m).
O mesmo padrão com (?m)
e usando String.matches
parece não funcionar.
Tenho certeza de que estou perdendo alguma coisa, mas não faço ideia do que. Não sou muito bom em expressões regulares.
Isto é o que eu tentei
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*(.*)
. A partir da resposta de @Amarghosh, obtive o padrãoUser Comments: [\\s\\S]*
. Entre essas, existe uma maneira melhor ou recomendada ou existem apenas duas maneiras diferentes de fazer o mesmo?[\s\S]
é um pouco mais explícito ("corresponde a qualquer caractere que seja espaço em branco ou não em branco"),.
é mais fácil de ler, mas você precisa procurar o modificador(?s)
ouDOTALL
para descobrir se as novas linhas estão incluídas ou não. Eu prefiro.
com oPattern.DOTALL
conjunto de sinalizador (isso é mais fácil de ler e se lembrar do que(?s)
na minha opinião, você deve usar o que você se sinta mais confortável...*
comDOTALL
é mais legível. Eu usei o outro para mostrar que o problema está nas diferenças entre str.matches e matcher.find e não nos sinalizadores. +1.*
comPattern.DOTALL
, mas vou ter que ir com (?) Porque preciso usarString.matches
.Isso não tem nada a ver com a bandeira MULTILINE; o que você está vendo é a diferença entre os métodos
find()
ematches()
.find()
terá êxito se uma correspondência puder ser encontrada em qualquer lugar da sequência de destino , enquantomatches()
espera que o regex corresponda à sequência inteira .Além disso,
MULTILINE
não significa o que você pensa que faz. Muitas pessoas parecem chegar à conclusão de que você precisa usar esse sinalizador se a sequência de destino contiver novas linhas - ou seja, se contiver várias linhas lógicas. Eu já vi várias respostas aqui no SO para esse efeito, mas, na verdade, tudo o que a flag faz é alterar 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 vamos deixar isso de lado por enquanto). Mas se a sequência contiver novas linhas, você poderá escolher^
e$
corresponder no início e no final de qualquer linha lógica, não apenas no início e no final de toda a sequência, configurando o sinalizador MULTILINE.Portanto, esqueça o que
MULTILINE
significa e lembre-se do que ele faz : altera o comportamento das âncoras^
e$
.DOTALL
O modo foi originalmente chamado de "linha única" (e ainda existe em alguns tipos, incluindo Perl e .NET), e sempre causou confusão semelhante. Temos a sorte de que os desenvolvedores Java tenham o nome mais descritivo nesse caso, mas não havia alternativa razoável para o modo "multilinha".No Perl, onde toda essa loucura começou, eles admitiram seu erro e se livraram dos modos "multilinha" e "linha única" nas expressões regulares do Perl 6. Em outros vinte anos, talvez o resto do mundo tenha seguido o exemplo.
fonte
str.matches(regex)
se comporta como oPattern.matches(regex, str)
que tenta corresponder toda a sequência de entrada ao padrão e retornaConsiderando que as
matcher.find()
tentativas de encontrar a próxima subsequência da sequência de entrada que corresponde ao padrão e retornamAssim, o problema está no regex. Tente o seguinte.
Portanto, em resumo, a
(\\W)*(\\S)*
parte do seu primeiro regex corresponde a uma sequência vazia, pois*
significa zero ou mais ocorrências, e a sequência real correspondida éUser Comments:
e não a sequência toda, como seria de esperar. O segundo falha quando tenta corresponder a toda a cadeia, mas não pode\\W
corresponder a um caractere que não seja da palavra, ou seja,[^a-zA-Z0-9_]
e o primeiro caractere éT
um caractere da palavra.fonte
User Comments: [\\s\\S]*
e isso funcionou. (obrigado!) Pela resposta do @Tim, obtive o padrãoUser Comments:(.*)
, isso também está correto. Agora, existe uma maneira recomendada ou melhor entre essas ou apenas duas maneiras de fazer a mesma coisa?(.*)
, juntamente comDOTALL
bandeira é mais óbvio / legível do que([\\s\\S]*)
O sinalizador de múltiplas linhas informa ao regex para corresponder o padrão a cada linha, em oposição a toda a cadeia de caracteres para os seus propósitos, um curinga será suficiente.
fonte