Espressione regolare per numeri in virgola mobile


115

Ho un compito per abbinare i numeri in virgola mobile. Ho scritto la seguente espressione regolare per questo:

[-+]?[0-9]*\.?[0-9]*

Ma restituisce un errore:

Invalid escape sequence (valid ones are  \b  \t  \n  \f  \r  \"  \'  \\ )

Per quanto ne so, dobbiamo usare anche un carattere di fuga per .. Per favore correggimi dove sbaglio.


10
In che lingua è usata questa regex?
CaffGeek

3
@JDB - Perché stai regalando 100 punti per un'espressione regolare numero / float? Lo standard è sempre stato (?:\d+(?:\.\d*)?|\.\d+)ed è stato pubblicato ad infinitum su SO ...


1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?se vuoi catturare anche la notazione esponenziale, e, g, 3.023e-23
wcochran

In alcuni linguaggi come Java o C ++, il backslash deve essere sottoposto a escape. Quindi per ottenere la regex "\.", Dovresti usare la stringa "\\.". Python aggira questo problema usando stringhe grezze.
HackerBoss

Risposte:


258

TL; DR

Utilizzare [.]invece di \.e [0-9]invece di \dper evitare problemi di escape in alcuni linguaggi (come Java).

Grazie a quello senza nome per averlo riconosciuto originariamente.

Un modello relativamente semplice per la corrispondenza di un numero in virgola mobile è

[+-]?([0-9]*[.])?[0-9]+

Questo corrisponderà:

  • 123
  • 123.456
  • .456

Guarda un esempio funzionante

Se vuoi anche trovare una corrispondenza 123.(un punto senza parte decimale), allora avrai bisogno di un'espressione leggermente più lunga:

[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)

Vedi la risposta di pkeller per una spiegazione più completa di questo schema

Se desideri includere numeri non decimali, come esadecimali e ottali, vedi la mia risposta a Come faccio a identificare se una stringa è un numero? .

Se vuoi convalidare che un input è un numero (piuttosto che trovare un numero all'interno dell'input), allora dovresti circondare il pattern con ^e $, in questo modo:

^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$

Espressioni regolari irregolari

Le "espressioni regolari", implementate nella maggior parte dei linguaggi moderni, API, framework, librerie, ecc., Sono basate su un concetto sviluppato nella teoria del linguaggio formale . Tuttavia, gli ingegneri del software hanno aggiunto molte estensioni che portano queste implementazioni ben oltre la definizione formale. Quindi, sebbene la maggior parte dei motori di espressioni regolari si somiglino, in realtà non esiste uno standard. Per questo motivo, molto dipende da quale lingua, API, framework o libreria stai utilizzando.

(Per inciso, per ridurre la confusione, molti hanno deciso di utilizzare " regex " o " regexp " per descrivere questi linguaggi di corrispondenza migliorati. Per ulteriori informazioni, vedere Espressione regolare è uguale a un'espressione regolare? Su RexEgg.com.)

Detto questo, la maggior parte dei motori regex (in realtà, tutti, per quanto ne so) accetterebbe \.. Molto probabilmente, c'è un problema con la fuga.

Il problema della fuga

Alcuni linguaggi hanno il supporto integrato per le espressioni regolari, come JavaScript . Per quelle lingue che non lo fanno, la fuga può essere un problema.

Questo perché fondamentalmente stai codificando in una lingua all'interno di una lingua. Java, ad esempio, utilizza \come carattere di escape all'interno delle sue stringhe, quindi se desideri inserire un carattere letterale barra rovesciata all'interno di una stringa, devi eseguirne l'escape:

// creates a single character string: "\"
String x = "\\";

Tuttavia, le espressioni regolari usano anche il \carattere per l'escape, quindi se vuoi abbinare un \carattere letterale , devi eseguirne l'escape per il motore regexe, quindi eseguire nuovamente l'escape per Java:

// Creates a two-character string: "\\"
// When used as a regex pattern, will match a single character: "\"
String regexPattern = "\\\\";

Nel tuo caso, probabilmente non sei sfuggito al carattere backslash nel linguaggio in cui stai programmando:

// will most likely result in an "Illegal escape character" error
String wrongPattern = "\.";
// will result in the string "\."
String correctPattern = "\\.";

Tutta questa fuga può creare molta confusione. Se la lingua con cui stai lavorando supporta stringhe grezze , dovresti usarle per ridurre il numero di barre rovesciate, ma non tutte le lingue lo fanno (in particolare: Java). Fortunatamente, c'è un'alternativa che funzionerà qualche volta:

String correctPattern = "[.]";

Per un motore regex, \.e [.]significa esattamente la stessa cosa. Nota che questo non funziona in tutti i casi, come newline ( \\n), parentesi quadra aperta ( \\[) e backslash ( \\\\o [\\]).

Una nota sui numeri corrispondenti

(Suggerimento: è più difficile di quanto pensi)

Abbinare un numero è una di quelle cose che penseresti sia abbastanza facile con regex, ma in realtà è piuttosto complicato. Diamo un'occhiata al tuo approccio, pezzo per pezzo:

[-+]?

Abbina un -o+

[0-9]*

Trova 0 o più cifre sequenziali

\.?

Abbina un optional .

[0-9]*

Trova 0 o più cifre sequenziali

Innanzitutto, possiamo ripulire un po 'questa espressione usando una scorciatoia di classe di caratteri per le cifre (nota che questo è anche suscettibile al problema di escape menzionato sopra):

[0-9] = \d

Lo userò di \dseguito, ma tieni presente che significa la stessa cosa di [0-9]. (Beh, in realtà, in alcuni motori \dcorrisponderà alle cifre di tutti gli script, quindi corrisponderà più di quanto [0-9]farà, ma probabilmente non è significativo nel tuo caso.)

Ora, se lo guardi attentamente, ti renderai conto che ogni singola parte del tuo modello è opzionale . Questo modello può corrispondere a una stringa di lunghezza 0; una stringa composta solo da +o -; oppure, una stringa composta solo da a .. Questo probabilmente non è quello che intendevi.

Per risolvere questo problema, è utile iniziare "ancorando" la tua regex con la stringa minima richiesta, probabilmente una singola cifra:

\d+

Ora vogliamo aggiungere la parte decimale, ma non va dove pensi che potrebbe:

\d+\.?\d* /* This isn't quite correct. */

Ciò corrisponderà comunque a valori come 123.. Peggio ancora, ha una sfumatura malvagia . Il punto è facoltativo, il che significa che hai due classi ripetute fianco a fianco ( \d+e \d*). Questo può effettivamente essere pericoloso se usato nel modo sbagliato, aprendo il tuo sistema ad attacchi DoS.

Per risolvere questo problema, invece di considerare il punto come opzionale, dobbiamo trattarlo come richiesto (per separare le classi di caratteri ripetute) e invece rendere facoltativa l'intera parte decimale:

\d+(\.\d+)? /* Better. But... */

Adesso sta meglio. Abbiamo bisogno di un punto tra la prima sequenza di cifre e la seconda, ma c'è un difetto fatale: non possiamo abbinare .123perché ora è richiesta una cifra iniziale.

Questo in realtà è abbastanza facile da risolvere. Invece di rendere facoltativa la parte "decimale" del numero, dobbiamo considerarla come una sequenza di caratteri: 1 o più numeri che possono essere preceduti da un .che può essere preceduto da 0 o più numeri:

(\d*\.)?\d+

Ora aggiungiamo solo il segno:

[+-]?(\d*\.)?\d+

Ovviamente, quelle barre sono piuttosto fastidiose in Java, quindi possiamo sostituirle nelle nostre classi di caratteri in forma lunga:

[+-]?([0-9]*[.])?[0-9]+

Corrispondenza e convalida

Questo è emerso nei commenti un paio di volte, quindi aggiungo un addendum sulla corrispondenza rispetto alla convalida.

L'obiettivo della corrispondenza è trovare del contenuto all'interno dell'input ("l'ago in un pagliaio"). L'obiettivo della convalida è garantire che l'input sia nel formato previsto.

Le espressioni regolari, per loro natura, corrispondono solo al testo. Dato un input, troveranno del testo corrispondente o non lo faranno. Tuttavia, "agganciando" un'espressione all'inizio e alla fine dell'input con i tag di ancoraggio ( ^e $), possiamo garantire che non venga trovata alcuna corrispondenza a meno che l'intero input non corrisponda all'espressione, utilizzando effettivamente le espressioni regolari per convalidare .

L'espressione regolare descritta sopra ( [+-]?([0-9]*[.])?[0-9]+) corrisponderà a uno o più numeri all'interno di una stringa di destinazione. Quindi, dato l'input:

apple 1.34 pear 7.98 version 1.2.3.4

L'espressione regolare corrisponderà 1.34, 7.98, 1.2, .3e .4.

Per convalidare che un dato input sia un numero e nient'altro che un numero, "aggancia" l'espressione all'inizio e alla fine dell'input avvolgendola in anchor tag:

^[+-]?([0-9]*[.])?[0-9]+$

Questo troverà una corrispondenza solo se l'intero input è un numero in virgola mobile e non troverà una corrispondenza se l'input contiene caratteri aggiuntivi. Quindi, dato l'input 1.2, verrà trovata una corrispondenza, ma apple 1.2 pearnon verrà trovata nessuna corrispondenza.

Nota che alcuni motori di regex hanno una validate, isMatcho una funzione simile, che fa in sostanza quello che ho descritto automaticamente, tornando truese viene trovata una corrispondenza e falsese non viene trovata alcuna corrispondenza. Inoltre, tieni presente che alcuni motori ti consentono di impostare flag che cambiano la definizione di ^e $, facendo corrispondere l'inizio / fine di una riga piuttosto che l'inizio / fine dell'intero input. In genere non è l'impostazione predefinita, ma fai attenzione a queste bandiere.


2
JDB, grazie e spero che tu sia ancora in giro! Sto leggendo il tuo post in futuro :) La tua risposta si prende sicuramente cura di 0.24 e 2.2 e non consente correttamente 4.2.44 Tutti testati con regex101.com Tuttavia, non consente 123. che come dici potrebbe essere accettabile (e penso che sia è!). Posso risolvere questo problema cambiando la tua espressione in [- +]? (\ D * [.])? \ D * (nota * alla fine invece di +) ma poi cose folli come. (il tuo secondo esempio) sono consentiti. Comunque avere la mia torta e mangiarla anche tu?
Dave

2
@Dave -\d+(\.\d*)?|\.\d+
JDB ricorda ancora Monica

/[-+]?(\d*[.])?\d+/.test("1.bc") // returns true
yeouuu

1
@yeouuu sì, perché 1.corrisponde. Aggiungi ^e $all'inizio e alla fine della regex se vuoi trovare una corrispondenza solo se l'intero input corrisponde.
JDB ricorda ancora Monica il

5
i float possono avere esponenti o essere NaN / Inf, quindi userei questo:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))e / d per float / double precision float. Non dimenticare un fold case flag alla regex
Markus Schmassmann

23

Non penso che nessuna delle risposte su questa pagina al momento della scrittura sia corretta (anche molti altri suggerimenti altrove su SO sono sbagliati). La complicazione è che devi abbinare tutte le seguenti possibilità:

  • Nessun punto decimale (cioè un valore intero)
  • Cifre sia prima che dopo il punto decimale (ad es 0.35. 22.165)
  • Solo cifre prima del punto decimale (ad es 0.. 1234.)
  • Solo cifre dopo il punto decimale (ad es .0. .5678)

Allo stesso tempo, devi assicurarti che da qualche parte sia presente almeno una cifra, ovvero non sono consentite le seguenti:

  • un punto decimale da solo
  • un punto decimale con segno senza cifre (cioè +.o -.)
  • +o -da soli
  • una stringa vuota

All'inizio sembra complicato, ma un modo per trovare l'ispirazione è guardare la fonte OpenJDK per il java.lang.Double.valueOf(String)metodo (inizia a http://hg.openjdk.java.net/jdk8/jdk8/jdk , fai clic su "sfoglia", scorri verso il basso /src/share/classes/java/lang/e trova la Doubleclasse). La lunga regex che questa classe contiene soddisfa varie possibilità che l'OP probabilmente non aveva in mente, ma ignorando per semplicità le parti di essa che si occupano di NaN, infinito, notazione esadecimale ed esponenti, e usando \dinvece della notazione POSIX per una singola cifra, posso ridurre le parti importanti della regex per un numero in virgola mobile con segno senza esponente a:

[+-]?((\d+\.?\d*)|(\.\d+))

Non credo che ci sia un modo per evitare la (...)|(...)costruzione senza consentire qualcosa che non contiene cifre, o vietando una delle possibilità che non ha cifre prima del punto decimale o nessuna cifra dopo di esso.

Ovviamente in pratica dovrai occuparti degli spazi vuoti finali o precedenti, sia nella regex stessa che nel codice che lo utilizza.


Se aggiungi il requisito per abbinare numeri come 123., allora sì ... l'opzione o è l'unica soluzione, come ho sottolineato in un commento al mio post originale.
JDB ricorda ancora Monica il

1
Questa e tutte le altre risposte ignorano che un float può avere un esponente.
NateS

1
@NateS Esatto, ho scritto "ignorando per semplicità le parti che si occupano di NaN, infinito, notazione esadecimale ed esponenti", perché sembra corrispondere allo scopo della domanda dell'OP. Ci sono implementazioni più complete in giro, inclusa quella che ho trovato nel codice sorgente JDK.
pkeller

1
La regex può [+-]?((?=\.?\d)\d*\.?\d*)essere usata per evitare l'alternanza? Usa un lookahead ...
4esn0k

1
@ 4esn0k Bella regex! Ci ho giocato e funziona. Ho due avvertenze: (1) non tutti i motori regex supportano asserzioni di larghezza zero (sebbene la maggior parte di quelli moderni lo faccia, AFAIK) e (2) il look-ahead è solo un'alternanza con un altro nome: il motore deve ancora provare qualcosa e torna indietro se non funziona. Avere comunque un voto positivo per un'idea molto chiara.
pkeller

7

quello che ti serve è:

[\-\+]?[0-9]*(\.[0-9]+)?

Sono sfuggito al segno "+" e "-" e ho anche raggruppato il decimale con le cifre seguenti poiché qualcosa come "1." non è un numero valido.

Le modifiche ti permetteranno di abbinare interi e float. per esempio:

0
+1
-2.0
2.23442

Il problema con questa espressione è che .1non sarebbe consentito, anche se tale input è universalmente riconosciuto come corretto.
JDB ricorda ancora Monica il

Questo ora accetterà stringhe di lunghezza zero -e +, che non sono numeri. Regex è complicato! :)
JDB ricorda ancora Monica il

Inoltre, questo non risponde alla domanda effettiva dell'OP, che \.non funziona.
JDB ricorda ancora Monica il

7

Voglio abbinare ciò che la maggior parte delle lingue considera numeri validi (interi e float):

  • '5' / '-5'

  • '1.0' / '1.' / '.1' / '-1.' / '-.1'

  • '0.45326e+04', '666999e-05', '0.2e-3', '-33.e-1'

Appunti:

  • preceding sign of number ('-' or '+') is optional

  • '-1.' and '-.1' are valid but '.' and '-.' are invalid

  • '.1e3' is valid, but '.e3' and 'e3' are invalid

Per supportare sia "1." e ".1" abbiamo bisogno di un operatore OR ("|") per assicurarci di escludere "." dalla corrispondenza.

[+-]?+/- sing è opzionale poiché ?significa 0 o 1 corrispondenze

( dato che abbiamo 2 sottoespressioni dobbiamo metterle tra parentesi

\d+([.]\d*)?(e[+-]?\d+)? Questo è per i numeri che iniziano con una cifra

| separa le sottoespressioni

[.]\d+(e[+-]?\d+)? questo è per i numeri che iniziano con "."

) fine delle espressioni

  • Per i numeri che iniziano con "."

[.] il primo carattere è un punto (tra parentesi o è un carattere jolly)

\d+ una o più cifre

(e[+-]?\d+)? questa è una notazione scientifica opzionale (0 o 1 corrispondenze a causa della fine di "?")

  • Per i numeri che iniziano con una cifra

\d+ una o più cifre

([.]\d*)? opzionalmente possiamo avere un punto carattere uno zero o più cifre dopo di esso

(e[+-]?\d+)? questa è una notazione scientifica opzionale

  • Notazione scientifica

e letterale che specifica l'esponente

[+-]? segno esponente opzionale

\d+ una o più cifre

Tutti quelli combinati:

[+-]?(\d+([.]\d*)?(e[+-]?\d+)?|[.]\d+(e[+-]?\d+)?)

Per accettare Eanche:

[+-]?(\d+([.]\d*)?([eE][+-]?\d+)?|[.]\d+([eE][+-]?\d+)?)

( Casi di test )


4

Questo è semplice: hai usato Java e dovresti usare \\.invece di \.(cerca caratteri che escono in Java).


Probabilmente hai ragione ... il messaggio di errore sembra un errore di sintassi del linguaggio di programmazione piuttosto che un errore del parser regex.
JDB ricorda ancora Monica

3

Questo ha funzionato per me:

(?P<value>[-+]*\d+\.\d+|[-+]*\d+)

Puoi anche usare questo (senza parametro denominato):

([-+]*\d+\.\d+|[-+]*\d+)

Usa un tester di regex online per testarlo (es. Regex101)


2
^[+]?([0-9]{1,2})*[.,]([0-9]{1,1})?$

Questo corrisponderà:

  1. 1.2
  2. 12.3
  3. 1,2
  4. 12,3

Sebbene questo frammento di codice sia il benvenuto e possa fornire qualche aiuto, sarebbe notevolmente migliorato se includesse una spiegazione di come e perché questo risolve il problema. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che lo chiede ora! Si prega di modificare la risposta di aggiungere una spiegazione, e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Toby Speight

oh grazie, me ne sto innamorando
Serg Burlaka

0
[+-]?(([1-9][0-9]*)|(0))([.,][0-9]+)?

[+-]? - segno iniziale opzionale

(([1-9][0-9]*)|(0)) - numero intero senza zero iniziale, compreso il singolo zero

([.,][0-9]+)? - parte frazionaria opzionale


1
Fornisci maggiori informazioni: per le persone che non conoscono le espressioni regolari si tratta di hyerogliph. Per le persone che li conoscono, non ne hanno bisogno.
peterh - Ripristina Monica

0

In C ++ utilizzando la libreria regex

La risposta sarebbe questa:

[0-9]?([0-9]*[.])?[0-9]+

Nota che non prendo il simbolo del segno, se lo volessi con il simbolo del segno, farebbe questo:

[+-]?([0-9]*[.])?[0-9]+

Questo separa anche un numero regolare o un numero decimale.


0

Nella notazione c, il numero float può verificarsi nelle seguenti forme:

  1. 123
  2. 123.
  3. 123.24
  4. .24
  5. 2e-2 = 2 * 10 pow -2 = 2 * 0,1
  6. 4E + 4 = 4 * 10 pow 4 = 4 * 10000

Per creare un'espressione regolare float, creerò prima "int regular expresion variable":

(([1-9][0-9]*)|0) will be int

Ora scriverò piccoli blocchi di espressione regolare float - la soluzione è concatenarli con o il simbolo "|".

Bocconcini:

- (([+-]?{int}) satysfies case 1
- (([+-]?{int})"."[0-9]*)  satysfies cases 2 and 3
- ("."[0-9]*) satysfies case 4
- ([+-]?{int}[eE][+-]?{int}) satysfies cases 5 and 6

Soluzione finale (concanando piccoli pezzi):

(([+-]?{int})|(([+-]?{int})"."[0-9]*)|("."[0-9]*)|([+-]?{int}[eE][+-]?{int})


-1

per javascript

const test = new RegExp('^[+]?([0-9]{0,})*[.]?([0-9]{0,2})?$','g');

Che funzionerebbe per 1.23 1234.22 0 0.12 12

È possibile modificare le parti in {}per ottenere risultati diversi in lunghezza decimale e anche davanti al decimale. Viene utilizzato negli input per inserire il numero e controllare ogni input durante la digitazione consentendo solo ciò che passa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.