2010-01-27 10 views
5

Qui ci sono più post che catturano il valore, ma sto solo cercando di verificare se il valore è qualcosa. Più vagamente messo; Sto cercando di capire la differenza tra controllare un valore e "catturare" un valore. Nel caso corrente il valore sarebbe i seguenti formati di denaro accettabili:Qualcuno può spiegare una regex di soldi che controlla solo se il valore corrisponde ad un modello?

Here is a post che spiega alcuni su un regex di soldi, ma io non lo capisco un po '.

.50 
50 
50.00 
50.0 
$5000.00 
$.50 

Non voglio virgole (la gente dovrebbe sapere che è ridicolo).

La cosa che sto avendo problemi con sono:

  1. consentendo un $ alla partenza del valore (ma ancora opzionale)
  2. Tenendo conto solo 1 punto decimale (ma non permettendo a la fine)
  3. Capire come funziona all'interno
  4. Comprendere anche fuori per ottenere una versione normalizzata (solo cifre e un punto decimale opzionale) fuori di esso che strisce il simbolo del dollaro.

mio regex corrente (che ovviamente non funziona a destra) è:

# I'm checking the Boolean of the following: 
re.compile(r'^[\$][\d\.]$').search(value) 

(Nota: sto lavorando in Python)

+0

Pensi virgole nei valori monetari sono ridicoli? Molte culture scrivono i loro valori monetari usando una virgola invece di un decimale; per esempio, € 4,99. – Rob

+0

Nella tua domanda scrivi "Permetti solo 1 punto decimale (ma non lo permetti alla fine)", ma in un commento chiedi come farlo in modo che corrisponda alla fine ... quale vuoi? Entrambi? –

risposta

14

Supponendo che si desidera consentire $5. ma non 5., il seguente accetterà la vostra lingua:

money = re.compile('|'.join([ 
    r'^\$?(\d*\.\d{1,2})$', # e.g., $.50, .50, $1.50, $.5, .5 
    r'^\$?(\d+)$',   # e.g., $500, $5, 500, 5 
    r'^\$(\d+\.?)$',   # e.g., $5. 
])) 

Parti importanti per comprendere:

  • ^ e $ corrispondono rispettivamente all'inizio e alla fine della stringa di input.
  • \. corrisponde un punto letterale
  • \$ corrisponde a un segno del dollaro letterale
    • \$? corrisponde a un simbolo del dollaro o niente (cioè, un simbolo del dollaro opzionale)
  • \d partite ogni singola cifra (0-9)
    • \d* matche s tirature di zero o più cifre
    • \d+ partite tirature di una o più cifre
    • \d{1,2} corrisponde a qualsiasi singolo cifra o una corsa di due cifre

I sottopattern tra parentesi sono gruppi di cattura: tutto il testo nell'input corrispondente alla sottoespressione in un gruppo di cattura sarà disponibile in matchobj.group(index). Il simbolo del dollaro non verrà catturato perché è al di fuori delle parentesi.

Perché Python non supporta più gruppi di cattura con lo stesso nome (!!!) si deve la ricerca in matchobj.groups() per quello che non è None. Ciò significa anche che devi fare attenzione quando modifichi il pattern per utilizzare (?:...) per ogni gruppo tranne l'importo.

Ottimizzare bel test harness di Mark, otteniamo

for test, expected in tests: 
    result = money.match(test) 
    is_match = result is not None 
    if is_match == expected: 
     status = 'OK' 
     if result: 
     amt = [x for x in result.groups() if x is not None].pop() 
     status += ' (%s)' % amt 
    else: 
     status = 'Fail' 
    print test + '\t' + status 

uscita:

 
.50  OK (.50) 
50  OK (50) 
50.00 OK (50.00) 
50.0 OK (50.0) 
$5000 OK (5000) 
$.50 OK (.50) 
$5.  OK (5.) 
5.  OK 
$5.000 OK 
5000$ OK 
$5.00$ OK 
$-5.00 OK 
$5,00 OK 
     OK 
$  OK 
.  OK 
.5  OK (.5)
+2

+1 per testare effettivamente la tua soluzione. –

+0

Come faccio a fare $ 5. abbinare (tecnicamente posso permettere questo valore). – orokusaki

+0

Nella tua domanda hai affermato che non volevi consentire un periodo alla fine. –

3

Credo che la seguente espressione regolare soddisferà le vostre esigenze :

/^\$?(\d*(\.\d\d?)?|\d+)$/ 

Consente un '$' opzionale. Consente un decimale facoltativo, ma richiede almeno uno ma non più di due cifre dopo il decimale se è presente il decimale.

Modifica: Le parentesi esterne cattureranno l'intero valore numerico per te.

+0

Tutte le partite vengono restituite in un elenco. '.group (1)' restituisce l'intera stringa con corrispondenza, quindi i gruppi successivi restituiscono i gruppi corrispondenti all'interno. '.group (3)' dovrebbe restituire solo il decimale e le cifre dopo il decimale se sono presenti. Per i tuoi scopi, '.group (2)' dovrebbe sempre darti l'intero numero. – Aaron

+1

Il modello corrisponde sia alla stringa vuota sia a un segno di dollaro isolato. Con le espressioni regolari, ricorda che i quantificatori '*' e '?' Sempre * riescono. –

+0

gbacon: +1 Un buon suggerimento per i test extra ... Ho aggiunto anche questi ai miei test. –

4

Comprendere anche fuori per ottenere una versione normalizzata (solo cifre e un punto decimale facoltativo) fuori di esso che strisce il simbolo del dollaro.

Questo è anche conosciuto come "catturare" il valore;)

lavorando fuori ad esempio la base di Aaron:

/^\$?(\d+(?:\.\d{1,2})?)$/ 

Poi l'importo (senza il simbolo del dollaro) sarà nel gruppo di cattura 1 .

+0

Può essere utile notare che '(?:)' È un gruppo non catturante. – Joel

+0

Mi hai catturato! Ho dimenticato di aggiungere l'ultima parte. – Aaron

+0

Puoi modificare il tuo post se lo desideri. A proposito, ho modificato la mia domanda perché non ho menzionato il fatto che devo essere in grado di accettare anche $ .50 e .50. – orokusaki

6

Ecco un regex è possibile utilizzare:

regex = re.compile(r'^\$?(\d*(\d\.?|\.\d{1,2}))$') 

Ecco un banco di prova che ho usato per testarlo. Ho incluso tutti i tuoi test, più alcuni dei miei. Ho anche incluso alcuni test negativi, in quanto mi assicuro che non corrisponda a quando non dovrebbe essere tanto importante quanto fare in modo che corrisponda a quando dovrebbe.

tests = [ 
    ('.50', True), 
    ('50', True), 
    ('50.00', True), 
    ('50.0', True), 
    ('$5000', True), 
    ('$.50', True), 
    ('$5.', True), 
    ('$5.000', False), 
    ('5000$', False), 
    ('$5.00$', False), 
    ('$-5.00', False), 
    ('$5,00', False), 
    ('', False), 
    ('$', False), 
    ('.', False), 
] 

import re 
regex = re.compile(r'^\$?(\d*(\d\.?|\.\d{1,2}))$') 
for test, expected in tests: 
    result = regex.match(test) 
    is_match = result is not None 
    print test + '\t' + ('OK' if is_match == expected else 'Fail') 

per ottenere il valore senza il $, è possibile utilizzare il gruppo catturato:

print result.group(1) 
+0

thx 1+ anche sul tuo. – orokusaki

+0

Ho cambiato il mio commento per consentire $ 5. invece di rifiutarlo, in base al tuo commento alla risposta di gbacon. –