2013-07-10 10 views
9

non capisco il motivo per cui '(\s*)+' dà un errore 'nothing to repeat'. Allo stesso tempo, '(\s?)+' va bene.Esiste un motivo per cui regex python non deve compilare r '( s *) +'?

Ho scoperto che questo problema è noto da un po 'di tempo (ad esempio regex error - nothing to repeat) ma lo vedo ancora in Python 3.3.1.

Quindi mi chiedo se c'è una spiegazione razionale per questo comportamento.

In realtà voglio abbinare una linea di parole o numeri ripetuti, per esempio:

'foo foo foo foo' 

mi è venuta in mente questo:

'(\w+)\s+(\1\s*)+' 

E 'fallito a causa del secondo gruppo : (\1\s*)+ Nella maggior parte dei casi probabilmente non avrei più di 1 spazio tra le parole in modo che (\1\s?)+ funzionasse. Ai fini pratici di questa opzione dovrebbe funzionare anche (\1\s{0,1000})+

Aggiornamento: Penso che dovrei aggiungere che ho visto il problema in pitone solo. in Perl funziona:

`('foo foo foo foo' =~ /(\w+)\s+(\1\s*)+/) ` 
Non

sicuro che è equivalente ma vim funziona anche:

`\(\<\w\+\>\)\_s\+\(\1\_s*\)\+` 

Update2: ho trovato un'altra implementazione delle espressioni regolari per Python che si dice per sostituire ri corrente un giorno . Ho controllato e l'errore non si verifica per i casi problematici di cui sopra. Questo modulo deve essere installato separatamente. Può essere scaricato here o tramite PyPI

+0

Per risolvere il problema, provare questo: http://stackoverflow.com/questions/17202233/remove-all-replicas-of-a-string-more-than-x-characters-long-regex –

+1

Non so quale sia il problema di Python, funziona bene in perl e PowerShell. Si noti, tuttavia, che ciò che si possiede corrisponderebbe a cose come 'foo foofoo' anche se funzionasse. Sto assumendo che non è ciò che si vuole, dal momento che non stai corrispondenti 'foofoo' o' foofoofoo' (in altre parole, la prima istanza deve essere seguita da spazi, ma dopo che le parole possono essere uniti). Prova questa regex: '(\ w +) \ s + (\ 1 (\ s + | $)) +'. Sospetto che sia quello che vuoi veramente, e probabilmente Python non avrà problemi con esso. –

+0

@Adi grazie, questo è un buon punto e sì, questo è quello che voglio. Ma sfortunatamente questo non funziona ancora in python – Phoenix

risposta

6

Il problema che Python ha con questo è in primo luogo la questione nulla cresciuto nel post collegato. Se avete intenzione di avere almeno un carattere Suggerisco invece utilizzare:

(\s+)+ 

Ciò detto, anche in realtà non ha senso se chiedete (\s*)+ con l'idea che + richiede qualcosa di esistere, e * no. Non riesco però ha senso per abbinare ? sia, ma è possibile risolvere mentalmente dicendo che è una partita opzionale nel senso che se non trova uno si muove su, piuttosto che * che interpreta niente come un modello abbinato.

Tuttavia, se si vuole veramente a controllare che cosa problema di Python con qualcosa è Suggerisco di giocare con le gamme. Per esempio, sono arrivato alla mia conclusione utilizzando questi due esempi:

re.compile("(\s{1,})+") 

che va bene

re.compile("(\s{0,})+") 

che non riesce nella stessa maniera.

Per lo meno questo significa che non è un "bug" in Python. È una decisione progettuale consapevole che agisce su ogni modello di regex che concettualmente cade in questa stessa fossa.La mia ipotesi (verificata in alcuni ambienti diversi) è che (\s{0,})+ fallirà in modo affidabile perché ripete esplicitamente un elemento potenzialmente nullo.

Tuttavia, sembra che un numero di ambienti utilizzi * per indicare che una corrispondenza è facoltativa e Python non segue questa scelta. Ha senso per molti casi, ma occasionalmente porta a comportamenti strani. Penso che Guido abbia fatto la scelta giusta qui, poiché avere una presenza spaziale inconsistente significa che hai violato il lemma del pompaggio e il tuo pattern non è più privo di contesto.

In questo caso, probabilmente non importa molto, ma significa che ci sarebbe inevitabilmente un'ambiguità in quella regex che non poteva essere risolta.

Quindi hai avuto un problema, quindi hai scelto di usare regex per risolvere quel problema. Ora hai 2 problemi, C'est la vie.

+0

Grazie per i consigli! Ho provato cose simili per farlo funzionare. Capisco che '(\ s *) +' non ha molto senso. La cosa reale di cui ho bisogno è '(\ 1 \ s *) +'. E il motivo per cui ho posto questa domanda è perché vim gestisce questa espressione regolare: '\ (\ <\w\+\> \) \ _ s \ + \ (\ 1 \ _s * \) \ +' che ritengo equivalente (anche se non mi va bene nella regex di vim) – Phoenix

+0

@Phoenix Non del tutto equivalente, anche se abbastanza vicino. Vim gestisce \ s * un po 'diversamente.Se vuoi fare la stessa cosa, devi sostituire tutti i tuoi spazi multipli con spazi singoli e poi usare l'operatore '? –

+0

Ma ha senso se qualcosa precede il \ s nel gruppo di fiammiferi, come mostra il suo esempio, ma dice che fallisce anche in Python. '(\ 1 \ s *) +' corrisponde al valore del primo gruppo con o senza una qualsiasi quantità di spazi bianchi finali una o più volte, mentre '(\ 1 \ s +) +' corrisponderà solo se è seguito da spazi bianchi. Quindi è utile essere in grado di farlo. Tuttavia, sospetto che questo sia più vicino a ciò che vuole, ma il problema è che non corrisponderà alla ripetizione della parola alla fine della stringa, motivo per cui ho suggerito '(\ 1 (\ s * | $)) sopra. –

0

Slater ha fornito una buona panoramica del problema, ma volevo solo aggiungere che, se ci pensate, questo corrisponde teoricamente a un numero infinito di spazi vuoti sul primo spazio vuoto che incontra. Se si potesse compilare questa espressione, l'applicazione potrebbe benissimo semplicemente tradursi in un ciclo infinito prima del primo carattere è ancora visto. Quindi non solo non è un bug, è una buona cosa.

+0

Uhh ... No ... non sarebbe mai successo, ma è una buona cosa. –

+0

E qual è la differenza con '(\ s?) +' Che è ok ma può portare allo stesso ciclo infinito. Come ho risposto a Slater, ciò che mi infastidisce è che in vim regexp equivalente funziona bene: '\ (\ <\w\+\> \) \ _ s \ + \ (\ 1 \ _s * \) \ +' – Phoenix

+0

Non vedo come quello risulterebbe in un ciclo infinito. Per prima cosa '\ s *' corrisponde a zero o più caratteri di spazi bianchi (tanti quanti ce ne sono in ordine consecutivo), quindi + corrisponde a una o più volte. Da solo, con nient'altro all'interno delle parentesi, il + è ridondante (per quanto posso dire '(\ s *) +' fa esattamente la stessa cosa di '(\ s *)'), ma non lo faccio vedere un ciclo infinito. Altre lingue che sono in grado di analizzare questa regex sembrano essere d'accordo con me. –

Problemi correlati