2012-04-25 12 views
6

So che ci sono domande simili alle mie a cui è stata data una risposta, ma dopo averli letti non ho ancora la soluzione che sto cercando.Date di corrispondenza con espressioni regolari in Python?

Utilizzando Python 3.2.2, devo abbinare "Mese, giorno, anno" con il mese come stringa, Giorno con due cifre non superiori a 30, 31 o 28 per febbraio e 29 per febbraio in un anno bisestile . (In pratica un vero appuntamento e XHTML)

Questo è quello che ho finora:

pattern = "(January|February|March|April|May|June|July|August|September|October|November|December)[,][ ](0[1-9]|[12][0-9]|3[01])[,][ ]((19|20)[0-9][0-9])" 
expression = re.compile(pattern) 
matches = expression.findall(sampleTextFile) 

Io non sono ancora troppo familiarità con la sintassi regex così che io possa avere caratteri in là che sono inutili (il [ ,] [] per la virgola e gli spazi si sente come il modo sbagliato per farlo), ma quando provo a far corrispondere "January 26, 1991" nel mio file di testo di esempio, la stampa degli elementi in "partite" è ('Gennaio', '26', '1991', '19').

Perché il "19" in più compare alla fine?

Inoltre, quali cose potrei aggiungere o modificare nella mia espressione regolare che mi consenta di convalidare correttamente le date? Il mio piano adesso è quello di accettare quasi tutte le date e eliminarle successivamente usando costrutti di alto livello confrontando il raggruppamento del giorno con il mese e il raggruppamento dell'anno per vedere se il giorno dovrebbe essere < 31,30,29,28

Qualsiasi l'aiuto sarebbe molto apprezzato includendo critiche costruttive su come sto andando a progettare la mia espressione regolare.

+5

Perché hai bisogno di usare un'espressione regolare? (Ora hai due problemi ...) – geoffspear

+0

Credo che la citazione @Wooble si riferisca a "Alcune persone, quando si trovano di fronte a un problema, pensano" Lo so, userò le espressioni regolari ". Ora hanno due problemi. ' e sono propenso a essere d'accordo. Raccomando di estrarre una stringa e 2 numeri (magari con un'espressione regolare semplice e semplice, ma più probabilmente semplicemente dividendo la stringa in virgola) e quindi utilizzando datetime per verificare se la data è valida. –

+0

Grazie per il consiglio, ma questo è un compito a casa dove sono obbligato a fare un'espressione per abbinare le date. – ahabos

risposta

6

Ecco un modo per fare un'espressione regolare che corrisponderà qualsiasi data del formato desiderato (anche se si potrebbe, ovviamente, ottimizzare sia le virgole sono opzionali, aggiungere le abbreviazioni mese, e così via):

years = r'((?:19|20)\d\d)' 
pattern = r'(%%s) +(%%s), *%s' % years 

thirties = pattern % (
    "September|April|June|November", 
    r'0?[1-9]|[12]\d|30') 

thirtyones = pattern % (
    "January|March|May|July|August|October|December", 
    r'0?[1-9]|[12]\d|3[01]') 

fours = '(?:%s)' % '|'.join('%02d' % x for x in range(4, 100, 4)) 

feb = r'(February) +(?:%s|%s)' % (
    r'(?:(0?[1-9]|1\d|2[0-8])), *%s' % years, # 1-28 any year 
    r'(?:(29), *((?:(?:19|20)%s)|2000))' % fours) # 29 leap years only 

result = '|'.join('(?:%s)' % x for x in (thirties, thirtyones, feb)) 
r = re.compile(result) 
print result 

Poi siamo avere:

E qual è questa gloriosa espressione, si potrebbe chiedere?

>>> print result 
(?:(September|April|June|November) +(0?[1-9]|[12]\d|30), *((?:19|20)\d\d))|(?:(January|March|May|July|August|October|December) +(0?[1-9]|[12]\d|3[01]), *((?:19|20)\d\d))|(?:February +(?:(?:(0?[1-9]|1\d|2[0-8]), *((?:19|20)\d\d))|(?:(29), *((?:(?:19|20)(?:04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96))|2000)))) 

(inizialmente ho intenzione di fare un conteggio tongue-in-cheek delle possibili date, ma io fondamentalmente finito per scrittura a mano che tutta la faccenda lordo fatta eccezione per i multipli di quattro, in ogni caso.)

+0

Grazie per quello! Sto ancora esaminando le regexp che mi hai dato lentamente per analizzare e comprendere le singole componenti, ma vedo come il modo migliore per farlo sarebbe stato quello di raggruppare i mesi con fondamentalmente nessuna differenza oltre al nome e al separato febbraio da il resto e combacia con un'altra parte dell'espressione – ahabos

+0

Ho detto che non c'era "nessun modo semplice" per fare in modo che un'espressione regolare controllasse il mese rispetto alla data. Quindi hai mostrato come farlo ... nel modo più duro ... tu, signore, sei pazzo, ma è il buon tipo di follia. +1! Post scriptum Mi piace soprattutto il correttore anno bisestile. – steveha

+0

pattern = r '(% s) + (% s), *% s'% anni che mostrano errore per me .. pattern = '(% s) + (% s), *% s'% anni TipoErrore: argomenti non sufficienti per la stringa di formato – monkey

1

Python ha una data di parser come parte del modulo time:

import time 
time.strptime("December 31, 2012", "%B %d, %Y") 

Quanto sopra è tutto ciò che serve se il formato della data è sempre la stessa.

Quindi, nel codice di produzione reale, scriverei un'espressione regolare che analizza la data e quindi utilizzare i risultati dell'espressione regolare per creare una stringa di data sempre dello stesso formato.

Ora che hai detto, nei commenti, che questo è compito, pubblicherò un'altra risposta con suggerimenti sulle espressioni regolari.

+0

Sono obbligato ad usare espressioni regolari dato che questo è un compito a casa che sto lottando con – ahabos

+0

Questo crea un oggetto data se hai una stringa che è solo la data, ma non funziona come una "espressione regolare" da abbinare date in una stringa o in un testo più grande. – Suz

2

Ecco alcuni rapidi pensieri:

Tutti coloro che sta suggerendo di utilizzare qualcosa di diverso da espressioni regolari si sta dando molto buoni consigli. D'altra parte, è sempre un buon momento per saperne di più sulla sintassi delle espressioni regolari ...

Un'espressione tra parentesi quadre - [...] - corrisponde a qualsiasi singolo carattere all'interno di tali parentesi. Quindi scrivere [,], che contiene solo un singolo carattere, è esattamente identico a scrivere una semplice virgola senza ornamenti: ,.

Il metodo .findall restituisce un elenco di tutti i gruppi corrispondenti nella stringa. Un gruppo è identificato dai genitori - (...) - e contano da sinistra a destra, prima i più lontani. La vostra espressione finale è simile al seguente:

((19|20)[0-9][0-9]) 

Le parentesi più esterne corrisponde l'intero anno, e le parentesi all'interno corrispondono alle prime due cifre. Quindi, per una data come "1989", gli ultimi due gruppi di match saranno 1989 e 19.

+2

Digli come risolvere il problema, sui gruppi non corrispondenti. (?: 19 | 20) – ricochet1k

+0

Nah, ti lascio fare. Non sono sicuro che sia necessario "aggiustare", perché non c'è nulla di "rotto". Volevo solo spiegare il comportamento. – larsks

2

Un gruppo è identificato da parentesi (...) e conta da sinistra a destra, più esterno prima. La vostra espressione finale è simile al seguente:

((19|20)[0-9][0-9])

Le parentesi più esterne corrisponde l'intero anno, e le parentesi all'interno corrispondono alle prime due cifre. Quindi, per una data come "1989", i due gruppi di match saranno 1989 e 19. Dato che non vuoi il gruppo interno (le prime due cifre), dovresti invece usare un gruppo non catturante. I gruppi non acquisiti iniziano con ?:, utilizzato in questo modo: (?:a|b|c)

A proposito, c'è una buona documentazione su come utilizzare le espressioni regolari here.

0

Prima di tutto, come detto, non credo che l'espressione regolare sia la scelta migliore per risolvere questo problema, ma per rispondere alla tua domanda. Usando le parentesi, si suddivide la stringa in più sottogruppi e quando si chiama la funzione findall, si crea un elenco con tutto il gruppo corrispondente creato e la stringa corrispondente.

((19|20)[0-9][0-9]) 

Qui è il vostro problema, l'espressione regolare corrisponderà sia l'intero anno e 19 o 20 a seconda che l'inizio anno con 19 o 20.

+0

Le parentesi sono sbilanciate. – ricochet1k

+0

Grazie non l'ho visto. – danielz

1

Hai questa espressione regolare:

pattern = "(January|February|March|April|May|June|July|August|September|October|November|December)[,][ ](0[1-9]|[12][0-9]|3[01])[,][ ]((19|20)[0-9][0-9])" 

Una caratteristica delle espressioni regolari è una "classe di caratteri". I personaggi tra parentesi quadre formano una classe personaggio. Pertanto, [,] è una classe di caratteri corrispondente a un singolo carattere, , (una virgola). Potresti anche mettere la virgola.

Forse volevi rendere la virgola opzionale? Puoi farlo mettendo un punto interrogativo dopo: ,?

Tutto ciò che inserisci tra parentesi crea un "gruppo di corrispondenza". Penso che il misterioso extra "19" provenga da un gruppo di partite che non volevi avere. Si può fare un gruppo non-matching utilizzando questa sintassi: (?:

Così, ad esempio:

r'(?:red|blue) socks' 

Questo corrisponde a "calzini rossi" o "calzini blu", ma non fare un gruppo partita. Se poi mettere che dentro le parentesi tonde:

r'((?:red|blue) socks)' 

che avrebbe fatto un gruppo di partita, il cui valore sarebbe "red socks" o "blue socks"

Penso che se si applicano questi commenti per l'espressione regolare, funzionerà. È per lo più corretto ora.

Per quanto riguarda la convalida della data rispetto al mese, è ben oltre lo scopo di un'espressione regolare. Il tuo modello corrisponderà a "February 31" e non esiste un modo semplice per risolverlo.

Problemi correlati