2010-09-06 16 views
131

Sto cercando di abbinare un testo a più righe utilizzando java. Quando uso la classe Pattern con il modificatore Pattern.MULTILINE, sono in grado di eguagliare, ma io non sono in grado di farlo con (?m).Corrispondenza testo multilinea con espressione regolare

lo stesso modello con (?m) e utilizzando String.matches non sembra funzionare.

Sono sicuro che mi manca qualcosa, ma non ho idea di cosa. Non sono molto bravo alle espressioni regolari.

Questo è quello che ho cercato

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? 

risposta

224

In primo luogo, si utilizzano i modificatori in base a un'ipotesi errata.

Pattern.MULTILINE o (?m) dice Java per accettare le ancore ^ e $ per corrispondere all'inizio e alla fine di ogni riga (altrimenti corrispondono solo a inizio/fine l'intera stringa).

Pattern.DOTALL o (?s) indica a Java di consentire che il punto corrisponda ai caratteri di nuova riga.

In secondo luogo, nel tuo caso, l'espressione regolare non riesce perché si sta utilizzando il metodo matches(), che si aspetta che il regex per abbinare l'intera stringa - che ovviamente non funziona poiché ci sono alcuni personaggi a sinistra dopo (\\W)*(\\S)* sono abbinati .

Quindi, se siete semplicemente alla ricerca di una stringa che inizia con User Comments:, utilizzare l'espressione regolare

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

con l'opzione 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 conterrà quindi il testo dopo User Comments:

+0

Sto provando a trovare un pattern che corrisponda a qualsiasi stringa che inizia con "Commenti degli utenti:". Dopo questo "Commenti utente:" è qualcosa che un utente entra in una textarea, e quindi può contenere * qualsiasi * - anche nuove righe. Sembra che ho bisogno di imparare molto in regex ... – Nivas

+2

Questo funziona (grazie!) Ho provato il modello '(? S) Commenti degli utenti: \ s * (. *)'. Dalla risposta di @Amarghosh ho ottenuto il pattern 'Commenti utente: [\\ s \\ S] *'. Tra questi c'è un modo * migliore * o * consigliato * o sono solo due modi diversi di fare lo stesso? – Nivas

+2

Entrambi significano la stessa cosa; '[\ s \ S]' è un po 'più esplicito ("corrisponde a qualsiasi carattere che sia o spazi bianchi o non spazi bianchi"), '.' è più facile da leggere, ma è necessario cercare' (? s) ' o il modificatore 'DOTALL' per scoprire se le newline sono incluse o meno. Preferirei '.' con il set di simboli' Pattern.DOTALL' (è più facile da leggere e ricordare di '(? S)' a mio parere. Dovresti usare ciò che ritieni più comodo. –

15

str.matches(regex)behaves likePattern.matches(regex, str) che tenta di corrispondere l'intera sequenza di ingresso contro il modello e restituisce

true se, e solo se, l'intera ingresso sequenza corrisponde a questo modello di corrispondenza

Mentre matcher.find()attempts to find la prossima sottosequenza della sequenza di input che corrisponde al modello e restituisce

true se, e solo se, una sottosequenza della sequenza di input corrisponde a modello di questo matcher

Così il problema è con la regex. Prova quanto segue.

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 

Così, in breve, la parte (\\W)*(\\S)* nel vostro primo regex trova una stringa vuota come * significa zero o più occorrenze e la stringa vera abbinato è User Comments: e non l'intera stringa come ci si aspetterebbe. La seconda non riesce mentre cerca di far corrispondere l'intera stringa ma non può come \\W corrisponde a un carattere non word, ad esempio [^a-zA-Z0-9_] e il primo carattere è T, un carattere di parola.

+0

Voglio abbinare qualsiasi stringa che inizia con "Commenti utente", e la stringa può contenere anche nuove righe. Così ho usato il pattern 'Commenti utente: [\\ s \\ S] *' e questo ha funzionato. (grazie!) Dalla risposta di @Tim ho ottenuto il pattern 'Commenti utente: (. *)', anche questo è ok Ora, c'è un modo * consigliato * o * migliore * tra questi, o sono solo due modi di fare lo stesso? – Nivas

+0

@Nivas Non penso che ci sarebbe alcuna differenza nelle prestazioni; ma penso che '(. *)' insieme al flag 'DOTALL' sia più ovvio/leggibile di' ([\\ s \\ S] *) ' – Amarghosh

31

Questo non ha nulla a che fare con la bandiera MULTILINE; ciò che vedi è la differenza tra i metodi find() e matches(). find() ha esito positivo se è possibile trovare una corrispondenza in qualsiasi punto della stringa di destinazione, mentre matches() prevede che l'espressione regolare corrisponda a all'intera stringa.

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 

Inoltre, MULTILINE non significa quello che pensi lo fa. Molte persone sembrano saltare alla conclusione che devi usare quel flag se la tua stringa di destinazione contiene newline - cioè, se contiene più righe logiche. Ho visto diverse risposte qui su SO in tal senso, ma in realtà, tutto ciò che fa questo flag cambia il comportamento degli ancoraggi, ^ e $.

Normalmente ^ corrisponde all'inizio della stringa di destinazione e $ corrisponde alla fine (o prima di una nuova riga alla fine, ma per ora lasceremo quella parte). Ma se la stringa contiene newline, è possibile scegliere ^ e $ per corrispondere all'inizio e alla fine di qualsiasi linea logica, non solo l'inizio e la fine dell'intera stringa, impostando il flag MULTILINE.

Così dimenticare ciò che MULTILINEsignifica e basta ricordare ciò che fa : cambia il comportamento dei ^ e $ ancore. La modalità DOTALL era originariamente chiamata "single-line" (e si trova ancora in alcune versioni, tra cui Perl e .NET) e ha sempre causato confusione simile. Siamo fortunati che gli sviluppatori Java siano andati con il nome più descrittivo in quel caso, ma non c'era un'alternativa ragionevole per la modalità "multilinea".

In Perl, dove tutta questa follia è iniziata, hanno ammesso il loro errore e si sono sbarazzati delle modalità "multiline" e "single-line" nelle regex di Perl 6. In altri venti anni, forse il resto del mondo avrà seguito l'esempio.

+4

Difficile credere che abbiano usato il nome del metodo" #matches "per indicare" corrisponde a tutti "yikes – rogerdpack

Problemi correlati