J'essaie de faire correspondre un texte de plusieurs lignes en utilisant Java. Lorsque j'utilise la classe Pattern
avec le modificateur Pattern.MULTILINE
, je peux faire correspondre, mais je ne peux pas le faire avec (?m).
Le même modèle avec (?m)
et en utilisant String.matches
ne semble pas fonctionner.
Je suis sûr qu'il me manque quelque chose, mais je ne sais pas quoi. Suis pas très bon à des expressions régulières.
C'est ce que j'ai essayé
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?
Tout d'abord, vous utilisez les modificateurs sous une hypothèse incorrecte.
Pattern.MULTILINE
ou (?m)
indique à Java d'accepter que les ancres ^
et $
correspondent au début et à la fin de chaque ligne (sinon, elles ne correspondent qu'au début/à la fin de la chaîne complète).
Pattern.DOTALL
ou (?s)
indique également à Java d'autoriser le point à correspondre aux caractères de nouvelle ligne.
Deuxièmement, dans votre cas, l'expression rationnelle échoue car vous utilisez la méthode matches()
qui s'attend à ce qu'elle corresponde à la chaîne entière chaîne - qui, bien sûr, ne fonctionne pas car il reste quelques caractères après la correspondance de (\\W)*(\\S)*
.
Donc, si vous cherchez simplement une chaîne qui commence par User Comments:
, utilisez le regex
^\s*User Comments:\s*(.*)
avec l'option 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
contiendra alors le texte après User Comments:
Cela n’a rien à voir avec le drapeau MULTILINE; ce que vous voyez est la différence entre les méthodes find()
et matches()
. find()
réussit si une correspondance peut être trouvée n'importe où dans la chaîne cible, alors que matches()
s'attend à ce que l'expression rationnelle corresponde à la chaîne entière.
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
De plus, MULTILINE
ne signifie pas ce que vous pensez que cela fait. De nombreuses personnes semblent en venir à la conclusion que vous devez utiliser cet indicateur si votre chaîne cible contient des nouvelles lignes, c'est-à-dire si elle contient plusieurs lignes logiques. J'ai vu plusieurs réponses ici sur SO à cet effet, mais en fait, tout ce drapeau ne change que le comportement des ancres, ^
et $
.
Normalement, ^
correspond au tout début de la chaîne cible et $
à la fin (ou avant une nouvelle ligne à la fin, mais nous la laisserons de côté pour le moment). Toutefois, si la chaîne contient des nouvelles lignes, vous pouvez choisir de faire correspondre ^
et $
au début et à la fin de toute ligne logique, et pas seulement au début et à la fin de la chaîne entière, en définissant l'indicateur MULTILINE.
Donc oubliez ce que MULTILINE
signifie et souvenez-vous simplement de ce qu’il fait: modifie le comportement des ancres ^
et $
. Le mode DOTALL
s'appelait à l'origine "une seule ligne" (et existe toujours dans certaines versions, y compris Perl et .NET), et il a toujours provoqué une confusion similaire. Nous sommes chanceux que les développeurs Java aient opté pour un nom plus descriptif, mais il n’existait aucune alternative raisonnable au mode "multiligne".
En Perl, où toute cette folie a commencé, ils ont admis leur erreur et se sont débarrassés des modes "multiligne" et "simple ligne" dans les expressions rationnelles Perl 6. Dans vingt ans, le reste du monde aura peut-être emboîté le pas.
str.matches(regex)
_ { se comporte comme _ Pattern.matches(regex, str)
qui tente de faire correspondre toute la séquence d'entrée au modèle et renvoie
true
si, et seulement si, la séquence d'entrée entière correspond au modèle de ce matcher
Considérant que matcher.find()
_ { recherche) la sous-séquence suivante de la séquence d'entrée qui correspond au modèle et retourne
true
si, et seulement si, une sous-séquence de la séquence d'entrée correspond au modèle de ce matcher
Ainsi, le problème est avec la regex. Essayez ce qui suit.
String test = "User Comments: This is \t a\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
En bref, la partie (\\W)*(\\S)*
de votre première expression rationnelle correspond à une chaîne vide, car *
signifie zéro ou plusieurs occurrences et la chaîne réellement recherchée est User Comments:
et non la chaîne entière comme prévu. Le second échoue car il essaie de faire correspondre la chaîne entière, mais ne peut pas, car \\W
correspond à un caractère autre que Word, c'est-à-dire [^a-zA-Z0-9_]
, et le premier caractère est T
, un caractère Word.
Le drapeau multiligne indique à regex de faire correspondre le motif à chaque ligne, par opposition à la chaîne entière correspondant à vos besoins, un caractère générique suffit.