2010-09-06 19 views
131

Próbuję dopasować tekst wieloliniowy przy użyciu języka Java. Kiedy używać klasy Pattern z modyfikatorem Pattern.MULTILINE, jestem w stanie dopasować, ale nie jestem w stanie to zrobić z (?m).Dopasuj tekst wielowierszowy za pomocą wyrażeń regularnych

tego samego wzoru i korzystania z (?m)String.matches nie wydają się działać.

Jestem pewien, że czegoś mi brakuje, ale nie mam pojęcia co. Nie jestem zbyt dobry w wyrażeniach regularnych.

To co próbowałem

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? 

Odpowiedz

224

Po pierwsze, używasz modyfikatorów pod niepoprawnym założeniem.

Pattern.MULTILINE lub (?m) mówi Java, aby zaakceptować kotwice ^ i $ pasujące na początku i na końcu każdej linii (inaczej pasują tylko na początku/końca całego łańcucha).

Pattern.DOTALL lub (?s) mówi Javie, aby umożliwić dopasowanie kropki do znaków nowej linii.

Po drugie, w przypadku, regex nie działa, ponieważ używasz metody matches() który oczekuje, że regex dopasować całą ciąg - co oczywiście nie działa, ponieważ istnieją pewne znaki pozostawione po (\\W)*(\\S)* zostały dopasowane .

Więc jeśli jesteś po prostu patrząc na sznurku, który zaczyna się User Comments: użyj regex

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

z opcją 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 będzie zawierało tekst po User Comments:

+0

Próbuję znaleźć wzorzec, który pasowałby do dowolnego ciągu rozpoczynającego się od "Komentarze użytkowników:". Po tym "Komentarze użytkownika:" jest coś, co użytkownik wprowadza w obszarze tekstowym, a zatem może zawierać * cokolwiek * - nawet nowe linie. Wygląda na to, że muszę się dużo nauczyć w regex ... – Nivas

+2

To działa (dziękuję!) Próbowałem wzorca '(? S) Komentarze użytkownika: \ s * (. *)'. Z odpowiedzi na @Amarghosha otrzymałem wzorzec "Komentarze użytkownika: [\ \ s \\ S] *". Wśród nich jest * lepszy * lub * zalecany * sposób lub czy są to tylko dwa różne sposoby robienia tego samego? – Nivas

+2

Obaj mają na myśli to samo; '[\ s \ S]' jest nieco bardziej jednoznaczne ("dopasuj dowolny znak, który jest albo spacji, albo spoza spacji"), '.' jest łatwiejsze do odczytania, ale musisz poszukać' (? s) ' lub modyfikator 'DOTALL' w celu ustalenia, czy nowe linie są włączone czy nie. Wolałbym '.' z zestawem flag' Pattern.DOTALL' (jest to łatwiejsze do odczytania i zapamiętania niż '(? S)', powinieneś użyć tego, co czujesz się najlepiej z. –

15

str.matches(regex)behaves likePattern.matches(regex, str) który próbuje dopasować całą sekwencję wejściową do wzorca i zwraca

true wtedy i tylko wtedy, gdy cała wejście sekwencja pasuje do tego szablonu matchera

Zważywszy matcher.find()attempts to find kolejnym podciągiem ciągu wejściowego, który pasuje do wzorca i zwraca

true wtedy i tylko wtedy, o podciąg sekwencji wejściowej odpowiada wzór ten układ dopasowujący za

Zatem problem dotyczy regex. Spróbuj wykonać następujące czynności.

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 

Tak więc w skrócie, część (\\W)*(\\S)* w pierwszym regex dopasowuje pusty łańcuch jako * oznacza zero lub więcej wystąpień i prawdziwy dopasowany łańcuch jest User Comments: a nie cały ciąg jak można się spodziewać. Druga nie powiedzie się, ponieważ próbuje dopasować cały ciąg, ale nie może tak jak \\W pasować do nie-słownego znaku, tj. [^a-zA-Z0-9_], a pierwszy znak to T, znak słowny.

+0

Chcę dopasować dowolny ciąg rozpoczynający się od "Komentarze użytkownika", a ciąg może zawierać również znaki nowej linii. Więc użyłem wzorca "Komentarze użytkowników: [\ \ s \\ S] *" i to działało. (dzięki!) Z odpowiedzi @Tima otrzymałem wzorzec "Komentarze użytkownika: (. *)", to też jest ok. Czy jest wśród nich * zalecany * lub * lepszy * sposób, czy też są to tylko dwa sposoby robić to samo? – Nivas

+0

@Nivas Nie sądzę, że wystąpiłaby jakakolwiek różnica w wydajności; ale myślę, że '(. *)' wraz z flagą 'DOTALL' jest bardziej oczywiste/czytelne niż' ([\ \ s \\ S] *) ' – Amarghosh

31

Nie ma to nic wspólnego z flagą MULTILINE; to, co widzisz, to różnica między metodami find() i matches(). find() powiedzie się, jeśli dopasowanie zostanie znalezione w dowolnym miejscu ciągu docelowego, natomiastoczekuje, że wyrażenie regularne będzie pasować do całego ciągu znaków.

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 

Ponadto MULTILINE nie znaczy to, co myślisz, że to robi. Wiele osób zdaje się przeskakiwać do wniosku, że musisz użyć tej flagi, jeśli twój docelowy ciąg zawiera znaki nowej linii - to znaczy, jeśli zawiera wiele linii logicznych. Widziałem tutaj kilka odpowiedzi na temat SO, ale w rzeczywistości wszystkie te flagi zmieniają zachowanie kotwic, ^ i $.

Normalnie ^ pasuje do samego początku ciągu docelowego, a $ pasuje do samego końca (lub przed znakiem nowej linii na końcu, ale na razie zostawimy to na boku). Ale jeśli ciąg zawiera znaki nowej linii, możesz wybrać dla ^ i $ dopasowanie do początku i końca dowolnej linii logicznej, a nie tylko początek i koniec całego ciągu, przez ustawienie flagi MULTILINE.

więc zapomnieć o tym, co oznaczaMULTILINEi po prostu pamiętać, co to robi: zmienia zachowanie ^ i $ kotwic. Tryb DOTALL był pierwotnie nazywany "pojedynczą linią" (i nadal jest w niektórych smakach, w tym w Perlu i .NET), i zawsze powodował podobne zamieszanie. Mamy szczęście, że twórcy Javy poszli z bardziej opisową nazwą w tym przypadku, ale nie było rozsądnej alternatywy dla trybu "multiline".

W Perlu, gdzie zaczęło się to szaleństwo, przyznali się do błędu i pozbyli się zarówno trybów "wieloliniowych", jak i "jednoliniowych" w 6 wyrażeń regularnych Perla. Za następne dwadzieścia lat może reszta świata pójdzie w jego ślady.

+4

Trudno uwierzyć, że użyli nazwy metody" #matches "do oznaczenia" dopasuj wszystkie "yikes – rogerdpack

Powiązane problemy