2010-02-08 14 views
31

Come si scrive un'espressione regolare per far corrispondere due stringhe date, in qualsiasi posizione nella stringa?Espressione regolare per trovare due stringhe ovunque nell'input

Per esempio, se io sto cercando cat e mat, dovrebbe corrispondere:

The cat slept on the mat in front of the fire. 
At 5:00 pm, I found the cat scratching the wool off the mat. 

Non importa ciò che precede queste stringhe.

+0

come su ' "catmat" '- dovrebbe la regex abbinarlo? quelle parole dovrebbero essere parole intere? Che ne dite di "questioni spinose"? – Amarghosh

+1

No, non dovrebbe corrispondere a nessuno di questi. Proprio queste due parole, in questo ordine, precedevano e seguivano qualsiasi altro testo. –

+0

Vedi la mia modifica se stai usando il mio codice. – eyelidlessness

risposta

49
/^.*?\bcat\b.*?\bmat\b.*?$/m 

Uso della m modificatore (che garantisce la partita metacharacters inizio/fine sulla linea rompe piuttosto che proprio all'inizio e alla fine della stringa):

  • ^ abbinamento la riga che inizia
  • .*? corrisponde a qualsiasi elemento sulla linea prima ...
  • \b corrisponde ad un limite di parola la prima occorrenza di un limite di parole (come discusso @codaddict)
  • quindi la stringa cat e un altro limite di parola; si noti che i caratteri di sottolineatura vengono considerati come caratteri "word", quindi _cat_ sarebbe non corrispondenza *;
  • .*?: tutti i caratteri prima di ...
  • confine, mat, confine
  • .*?: eventuali caratteri rimanenti prima di ...
  • $: la fine della linea.

E 'importante utilizzare \b per assicurare le parole specificate non fanno parte di parole più lunghe, ed è importante utilizzare i caratteri jolly non avidi (.*?) contro avidi (.*) perché quest'ultimo avrebbe fallire su stringhe come " C'è un gatto in cima al tappeto che si trova sotto il gatto. " (E sarebbe partita l'ultima occorrenza di "gatto", piuttosto che il primo.)

* Se si vuole essere in grado di eguagliare _cat_, è possibile utilizzare:

/^.*?(?:\b|_)cat(?:\b|_).*?(?:\b|_)mat(?:\b|_).*?$/m 

che corrisponde sia sottolinea o confini delle parole attorno alle parole specificate. (?:) indica un gruppo non catturante, che può aiutare con le prestazioni o evitare catture in conflitto.

Modifica: nei commenti è stata sollevata una domanda sul fatto che la soluzione avrebbe funzionato per le frasi anziché solo per le parole. La risposta è assolutamente sì. Il seguente sarebbe partita "Una linea che comprende sia la prima frase e la seconda frase":

/^.*?(?:\b|_)first phrase here(?:\b|_).*?(?:\b|_)second phrase here(?:\b|_).*?$/m 

Edit 2: Se l'ordine non importa è possibile utilizzare:

/^.*?(?:\b|_)(first(?:\b|_).*?(?:\b|_)second|second(?:\b|_).*?(?:\b|_)first)(?:\b|_).*?$/m 

E se le prestazioni sono Davvero un problema qui, è possibile una soluzione (se il tuo motore regex lo supporta) potrebbe (ma probabilmente non lo farà) eseguire meglio di quanto sopra, ma lascerò sia la versione più complessa del test e il test delle prestazioni, quanto un esercizio alla interlocutore/lettore.

Modificato secondo il commento di @Alan Moore. Non ho avuto la possibilità di provarlo, ma ti crederò sulla parola.

+3

'[\ b]' corrisponde a un ** backspace **, non a un limite di parole; '\ b' assume un significato diverso all'interno di una classe di caratteri. –

+0

Il primo serve il mio scopo giusto. Grazie. –

+0

@Alan Moore, grazie per il suggerimento. Il modo in cui le cose cambiano significato in una classe di caratteri mi getta sempre per un giro. – eyelidlessness

2

Si può provare:

\bcat\b.*\bmat\b 

\b è un ancoraggio e corrisponde a un limite di parola . Cercherà parole gatto e stuoia ovunque nella stringa con gatto seguendo mat. Non corrisponde:

Therez caterpillar on the mat.

ma corrisponderà

The cat slept on the mat in front of the fire

Se si desidera far corrispondere le stringhe che hanno lettere gatto seguito da tappeto, si può provare:

cat.*mat 

Questo corrisponderà sia l'esempio di cui sopra stringhe.

+0

Hmm .. non del tutto. Non corrispondeva perfettamente a nessuna delle due corde. Corrisponde al pattern "cat ... mat", ma non alla parte precedente e successiva. –

+0

Oh ok ... se vuole cercare gatto e stuoia come * parole *, puoi aggiungere un limite di parole. Grazie Phanindra K. – codaddict

+0

Grazie codaddict. Ho modificato la regex in modo simile a questo:. *? Cat. *? Mat. *? Spero che non abbia effetti collaterali indesiderati. :) –

14
(.* word1.* word2.*)|(.* word2.* word1.*) 
+0

-1: corrisponde in modo non corretto a "un materasso catastrofico", non riesce su "gatto sul tappeto" e non osserva l'ordine delle parole (sebbene sia stato specificato solo nei commenti). –

+0

aggiungi i limiti di parola quindi non ci sono corrispondenze di sottostringhe – rxgx

+5

Questo potrebbe non corrispondere a ciò che OP voleva ma mi ha aiutato a trovare due stringhe in un URL così +1 –

1

non dovete usare espressioni regolari. Nella tua lingua preferita, dividi gli spazi, supera le parole suddivise, controlla gatto e tappeto. ad esempio in Python

>>> for line in open("file"): 
...  g=0;f=0 
...  s = line.split() 
...  for item in s: 
...   if item =="cat": f=1 
...   if item =="mat": g=1 
...  if (g,f)==(1,1): print "found: " ,line.rstrip() 

found: The cat slept on the mat in front of the fire. 
found: At 5:00 pm, I found the cat scratching the wool off the mat. 
+0

questo corrisponde anche a mat prima di cat, che è quello che la domanda chiede, ma potrebbe non essere l'intento :) – Jimmy

+0

anche, punteggiatura. – Jimmy

+0

Il problema che regex risolve in questo caso sta avendo molta più flessibilità su cosa considerare un confine. Considerando solo gli spazi per essere dei limiti, ciò fallirebbe (anche se sembra che l'intervistatore abbia intenzione di farlo coincidere): "Attenti al gatto; E mentre la regex può essere più lenta del codice equivalente per farlo in modo più sicuro, il codice equivalente potrebbe richiedere decine di righe di codice per farlo correttamente. C'è una cautela giustificata su SO riguardo regex, ma è davvero lo strumento appropriato per questo lavoro. – eyelidlessness

0

Questo funziona per la ricerca di file che contengono sia String1 e String2

(((|. \ N) ) String1 ((|. \ N)) Stringa2) | (((|. \ N) ) String2 ((|. \ n)) Stringa1)

Trova ogni numero di caratteri o campi di linea seguiti da String1 seguita da un numero qualsiasi di caratteri o la linea dei campi seguito da String2 o Trova ogni numero di caratteri o campi di linea fo llowed da String2 seguita da un numero qualsiasi di caratteri o campi di linea seguiti da String1

+0

Ciao, per lo stesso primo gatto e mat. potresti consigliare come eseguire la ricerca di espressioni regolari negative. per esempio. se trovo gatto e stuoino in fila. Dovrei ignorare quella linea. ma se trovo il gatto con qualsiasi altra cosa, dovrei catturarlo. Quindi, di seguito, è necessario prendere in considerazione un gatto con tappeto. 'Il gatto dormiva sul tappeto davanti al fuoco. ' Alle 17:00, ho trovato il gatto che grattava la lana dal tappeto' – enthuguy

2

Questo è abbastanza facile sulla potenza di elaborazione necessaria:

(string1(.|\n)*string2)|(string2(.|\n)*string1)

ho usato questo in Visual Studio 2013 per trovare tutti i file che conteneva entrambe le stringhe 1 e 2.

+0

Ottimo! Ma come posso evitare spazi bianchi e altri caratteri non di parola in mezzo. Sto cercando di trovare tutti i mix di parole intere con le parole "studente" e "nome". Quindi "studentpreferredname" e "student_name" sono ok, ma non "StudentID = @ StudentID ORDER BY q.QualificationName" – Fandango68

3

Se è assolutamente necessario utilizzare una sola espressione regolare allora

/(?=.*?(string1))(?=.*?(string2))/is 

i modificatore =

insensibile caso. *?valutazione pigra per qualsiasi carattere (corrisponde il meno possibile)

? = per Positivo Lookahead deve corrispondere un posto

s modificatore =. (punto) accetta anche le interruzioni di linea

Problemi correlati