2012-07-03 19 views
5

Esiste un metodo API che restituisce tutte le sottostringhe (possibilmente sovrapposte) che corrispondono a un'espressione regolare?Sottostringhe sovrapposte che corrispondono a un regex java

Ad esempio, ho una stringa di testo: String t = 04/31 412-555-1235; e ho uno schema: Pattern p = new Pattern("\\d\\d+"); che corrisponde a stringhe di due o più caratteri.

Le partite ottengo sono: 04, 31, 412, 555, 1235.

Come posso ottenere le partite che si sovrappongono?

voglio il codice per tornare: 04, 31, 41, 412, 12, 55, 555, 55, 12, 123, 1235, 23, 235, 35.

Teoricamente dovrebbe essere possibile - c'è un ovvio algoritmo O(n^2) che enumera e controlla tutte le sottostringhe sul modello.

EDIT

Piuttosto che enumerare tutte le stringhe, è più sicuro di utilizzare il metodo region(int start, int end) in Matcher. Il controllo del modello rispetto a una sottostringa separata e separata potrebbe modificare il risultato della corrispondenza (ad esempio se è presente un controllo di gruppo o di limite di parole non di cattura all'inizio/alla fine del modello).

EDIT 2

In realtà, non è chiaro se region() fa quello che ci si aspetta per le partite di lunghezza zero. Le specifiche sono vaghe e gli esperimenti danno risultati deludenti.

Ad esempio:

String line = "xx90xx"; 
String pat = "\\b90\\b"; 
System.out.println(Pattern.compile(pat).matcher(line).find()); // prints false 
for (int i = 0; i < line.length(); ++i) { 
    for (int j = i + 1; j <= line.length(); ++j) { 
    Matcher m = Pattern.compile(pat).matcher(line).region(i, j); 
    if (m.find() && m.group().size == (j - i)) { 
     System.out.println(m.group() + " (" + i + ", " + j + ")"); // prints 90 (2, 4) 
    } 
    } 
} 

Non sono sicuro di quale sia la soluzione più elegante è. Un approccio consisterebbe nel prendere una sottostringa di line e eseguire il rilievo con i caratteri di limite appropriati prima di verificare se la corrispondenza di pat corrisponde.

EDIT 3

Ecco la soluzione completa che mi è venuta. Può gestire modelli a larghezza zero, limiti, ecc. Nell'espressione regolare originale. Controlla tutte le sottostringhe della stringa di testo e controlla se l'espressione regolare corrisponde solo alla posizione specifica inserendo il modello con il numero appropriato di caratteri jolly all'inizio e alla fine. Sembra funzionare per i casi che ho provato - anche se non ho fatto test approfonditi. È sicuramente meno efficiente di quanto potrebbe essere.

public static void allMatches(String text, String regex) 
    { 
    for (int i = 0; i < text.length(); ++i) { 
     for (int j = i + 1; j <= text.length(); ++j) { 
     String positionSpecificPattern = "((?<=^.{"+i+"})("+regex+")(?=.{"+(text.length() - j)+"}$))"; 
     Matcher m = Pattern.compile(positionSpecificPattern).matcher(text); 

     if (m.find()) 
     { 
      System.out.println("Match found: \"" + (m.group()) + "\" at position [" + i + ", " + j + ")"); 
     } 
     } 
    } 
    } 

EDIT 4

Ecco un modo migliore di fare questo: https://stackoverflow.com/a/11372670/244526

EDIT 5

La biblioteca JRegex supporta trovare tutte le stringhe che si sovrappongono che corrispondono a un regex Java (anche se sembra non essere aggiornato da un po 'di tempo).In particolare, il documentation on non-breaking search specifica:

Utilizzando la ricerca non-breaking è possibile trovare tutte le possibili occureneces di un modello , compresi quelli che si intersecano o inserito. Questo è ottenuto utilizzando il metodo del Matcher procedere() al posto di find()

+0

basta fare un ciclo post-regex attraverso tutti i risultati di 3 o più caratteri –

+0

http://regexlib.com/ potrebbe essere un buon posto per fare un po 'di scavo. –

+0

@ Ωmega Provando il mio meglio, ma aperto a feedback che non è utile. Saluti. –

risposta

0

Il più vicino si può ottenere è qualcosa di simile.

"(?=((\\d*)\\d))(?=(\\d)\\d*)" 

Il risultato sarà nel gruppo di cattura di 1, 2 e 3.

quanto riguarda la mia immaginazione può andare, posso solo pensare di catturare in lunghezza zero affermazione come un valido modo per riconquistare la stessa posizione di una corda. Catturare il testo al di fuori dell'asserzione di lunghezza zero consumerà il testo una volta per tutte (il look-behind può solo acquisire una lunghezza fissa in Java, quindi può essere considerato inaccessibile).

Questa soluzione non è perfetta: oltre alla ripetizione (di testo nella stessa posizione!) E alle corrispondenze di stringa vuote, non acquisirà tutte le sottostringhe possibili.

Un modo per catturare tutte le possibili stringhe è costruire la seguente espressione regolare con valore di n a partire da 1:

"(?=(\\d{" + n + "}))" 

E hanno la stringa contro questo per incrementare il valore di n fino a quando non v'è alcuna corrispondenza.

Questo metodo è ovviamente inefficiente rispetto al metodo di abbinamento di tutti i numeri con "\ d +" ed estrae tutta la sottostringa.

0

E 'fattibile come O (n)solo se si specifica la gamma di lunghezza del numero consentito.

Diciamo da 2-4 cifre (numeri 00-9999): (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)

Questa è un'asserzione di lunghezza zero tramite lookahead positivo, catturando quali lookahead in gruppi. Il risultato è un array di tutte le stringhe di 2-4 cifre che possono essere trovate all'interno dell'ingresso regex, insieme a duplicati e stringhe vuote (per acquisizioni non corrispondenti).

Non sono uno sviluppatore Java, ma credo che uno script Perl possa essere letto anche come esempio.

#!/usr/bin/perl          # perl script 
use List::MoreUtils qw/ uniq /;      # uniq subroutine library 
$_ = '04/31 412-555-1235';       # input 
my @n = uniq (/(?=(\d{2}))(?=(\1\d)?)(?=(\2\d)?)/g); # regex (single slash in Perl) 
print "$_\n" for grep(/\S/, @n);      # print non-empty lines 

Il trucco sta usando backreference. Se si desidera acquisire una stringa di 2-5 cifre, è necessario utilizzare un altro aspetto positivo nella regex: (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)(?=(\\3\\d)?).

Credo che questo sia l'approccio più vicino che puoi fare. Se questo funziona per te, lascia un commento e spero che alcuni sviluppatori Java possano modificare la mia risposta con il codice Java per lo script sopra.

+0

Il regex è lo stesso in Java (ad eccezione del fatto che il backslash deve essere scappato). Per quanto riguarda 'uniq', può essere simulato con' Set' in Java ('TreeSet' o' HashSet'). – nhahtdh

+0

@nhahtdh - Grazie. Sentiti libero di aggiungere aggiornamenti alla mia risposta modificando il post. –

1

Ho affrontato una situazione simile e ho provato le risposte di cui sopra ma nel mio caso ci è voluto troppo tempo impostando l'indice di inizio e di fine del matcher ma penso di aver trovato una soluzione migliore, sono pubblicandolo qui per gli altri. Quindi qui sotto c'è il mio codice sniplet.

if (textToParse != null) { 
Matcher matcher = PLACEHOLDER_PATTERN.matcher(textToParse); 
    while(matcher.hitEnd()!=true){ 
     Boolean result = matcher.find(); 
     int count = matcher.groupCount(); 
     System.out.println("Result " +result+" count "+count); 
     if(result==true && count==1){ 
      mergeFieldName = matcher.group(1); 
      mergeFieldNames.add(mergeFieldName); 
      } 
     } 
    } 

ho usato il metodo matcher.hitEnd() per verificare se ho raggiunto la fine del testo.

Spero che questo aiuti. Grazie!

Problemi correlati