2010-09-06 14 views
131

Ich versuche, einen mehrzeiligen Text mit Java übereinstimmen. Als ich die Pattern Klasse mit dem Pattern.MULTILINE Modifikator, bin ich in der Lage zu passen, aber ich bin nicht in der Lage mit (?m). so zu tunÜbereinstimmung mehrzeiliger Text mit regulären Ausdruck

Das gleiche Muster mit (?m) und String.matches mit scheint nicht zu funktionieren.

Ich bin sicher, dass ich etwas vermisse, aber keine Ahnung was. Bin nicht gut in regulären Ausdrücken.

Das ist, was ich versuchte

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? 

Antwort

224

Zuerst verwenden Sie die Modifikatoren unter einer falschen Annahme.

Pattern.MULTILINE oder (?m) Java erzählt die Anker zu akzeptieren ^ und $ am Anfang und Ende jeder Zeile entsprechen (sonst nur sie am Anfang/Ende des gesamten Strings übereinstimmen).

Pattern.DOTALL oder (?s) teilt Java mit, dass der Punkt auch Newline-Zeichen entsprechen darf.

Zweitens in Ihrem Fall die Regex schlägt fehl, da Sie die matches() Methode verwenden, die die Regex die gesamte Zeichenfolge übereinstimmen erwartet - was natürlich nicht funktioniert, da es einige Zeichen übrig sind nach (\\W)*(\\S)* haben abgestimmt .

Also, wenn Sie einfach sich für einen String suchen, der mit User Comments: beginnt, verwenden Sie die regex

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

mit der Pattern.DOTALL Option:

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

ResultString enthält dann den Text nach User Comments:

+0

Ich versuche ein Muster zu finden, das zu jeder Zeichenfolge passt, die mit beginnt "Benutzerkommentare:" Nach diesem "User Comments:" gibt ein Benutzer ein Textfeld ein und kann daher * alles * - sogar neue Zeilen enthalten. Sieht so aus, als müsste ich viel in Regex lernen ... – Nivas

+2

Das funktioniert (danke!) Ich habe das Muster '(? S) User Comments: \ s * (. *)' Versucht. Aus der Antwort von @Amarghosh habe ich das Muster 'User Comments: [\\ s \\ S] *'. Unter diesen gibt es einen * besseren * oder * empfohlenen * Weg oder sind dies nur zwei verschiedene Möglichkeiten, das gleiche zu tun? – Nivas

+2

Sie beide gleich; '[\ s \ S]' ist etwas expliziter ("setze ein beliebiges Zeichen, das entweder Leerzeichen oder Nicht-Leerzeichen ist"), '.' ist einfacher zu lesen, aber du musst nach dem' (? s) 'suchen oder 'DOTALL', um herauszufinden, ob Zeilenumbrüche enthalten sind oder nicht. Ich bevorzuge '.' mit dem' Pattern.DOTALL' Flag Set (das ist meiner Meinung nach leichter zu lesen und zu erinnern als '(? S)'. Sie sollten das verwenden, womit Sie sich am wohlsten fühlen. –

15

str.matches(regex)behaves likePattern.matches(regex, str), die die gesamte Eingangssequenz mit dem Muster und

true wenn, und nur wenn der gesamten Eingang kehrt zu entsprechen versucht Sequenz entspricht dem Muster dieses Matcher

Während matcher.find()attempts to find die nächste Teilfolge der Eingangssequenz, das das Muster übereinstimmt und gibt

true wenn, und nur wenn eine Teilfolge der Eingangssequenz entspricht dieses Muster des Matcher

So Das Problem ist mit der Regex. Versuche Folgendes.

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 

So kurz gesagt, der (\\W)*(\\S)* Teil in Ihrem ersten Regex paßt eine leere Zeichenfolge als * bedeutet null oder mehr Vorkommen und die realen abgestimmte Saite ist User Comments: und nicht die ganze Zeichenfolge als man erwarten würde. Die zweite schlägt fehl, da sie versucht, die ganze Zeichenfolge abzugleichen, aber sie kann nicht als \\W mit einem Nicht-Wort-Zeichen übereinstimmen, dh [^a-zA-Z0-9_] und das erste Zeichen ist T, ein Wortzeichen.

+0

Ich möchte jede Zeichenfolge, die mit "User Comments" beginnt, und die Zeichenfolge kann auch Zeilenumbrüche enthalten. Also habe ich das Muster 'User Comments: [\\ s \\ S] *' benutzt und das hat funktioniert. (Danke!) Aus der Antwort von @Tim habe ich das Muster 'Benutzerkommentare: (. *)', das ist auch ok Nun, gibt es einen * empfohlenen * oder * besseren * Weg zwischen diesen, oder sind das nur zwei Wege das Gleiche zu tun? – Nivas

+0

@Nivas Ich glaube nicht, dass es einen Leistungsunterschied geben würde; aber ich denke '(. *)' zusammen mit 'DOTALL' Flag ist offensichtlicher/lesbarer als' ([\\ s \\ S] *) ' – Amarghosh

31

Das hat nichts mit der MULTILINE-Flagge zu tun; Was Sie sehen, ist der Unterschied zwischen den find() und matches() Methoden. find() erfolgreich, wenn eine Übereinstimmung irgendwo in der Zielzeichenfolge gefunden werden kann, während matches() erwartet, dass die Regex die gesamte Zeichenfolge entspricht.

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 

Außerdem MULTILINE bedeutet nicht, was Sie denken, es tut. Viele Leute scheinen zu der Schlussfolgerung zu gelangen, dass Sie dieses Flag verwenden müssen, wenn Ihre Zielzeichenfolge Zeilenumbrüche enthält - dh wenn sie mehrere logische Zeilen enthalten. Ich habe mehrere Antworten hier auf SO zu diesem Zweck gesehen, aber in der Tat ändert dieses Flag das Verhalten der Anker, ^ und $.

Normalerweise entspricht ^ dem Anfang der Zielzeichenfolge, und $ entspricht dem Ende (oder vor einem Zeilenende am Ende, aber wir lassen das für jetzt beiseite). Wenn die Zeichenfolge jedoch Zeilenumbrüche enthält, können Sie für ^ und $ auswählen, dass sie am Anfang und am Ende jeder logischen Zeile und nicht nur am Anfang und Ende der gesamten Zeichenfolge durch Setzen des MULTILINE-Flags übereinstimmen.

So etwa vergessen, was MULTILINE bedeutet und nur daran erinnern, was es tut: das Verhalten des ^ und $ Ankers ändert. DOTALL Modus wurde ursprünglich "Single-Line" (und ist immer noch in einigen Geschmacksrichtungen, einschließlich Perl und .NET), und es hat immer eine ähnliche Verwirrung verursacht. Wir haben Glück, dass die Java-Entwickler in diesem Fall mit dem besser beschreibenden Namen gingen, aber es gab keine vernünftige Alternative für den "Multiline" -Modus.

In Perl, wo all dieser Wahnsinn begann, haben sie ihren Fehler zugegeben und sowohl "Multiline" - als auch "Single-Line" -Modi in Perl 6 Regexes losgeworden. In weiteren zwanzig Jahren wird vielleicht der Rest der Welt ihm gefolgt sein.

+4

Kaum zu glauben, dass sie den Methodennamen" #matches "verwendeten, um" trifft alle "yikes zu bedeuten – rogerdpack

Verwandte Themen