Expression régulière pour les nombres à virgule flottante


115

J'ai une tâche pour faire correspondre les nombres à virgule flottante. J'ai écrit l'expression régulière suivante pour cela:

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

Mais, il renvoie une erreur:

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

Selon ma connaissance, nous devons utiliser un caractère d'échappement pour le .également. Veuillez me corriger là où je me trompe.


10
Dans quelle langue cette regex est-elle utilisée?
CaffGeek

3
@JDB - Pourquoi donnez-vous 100 points pour un nombre / float regex? La norme a toujours été (?:\d+(?:\.\d*)?|\.\d+)et a été publiée à l'infini sur SO ...


1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?si vous voulez aussi attraper la notation exponentielle, e, g, 3.023e-23
wcochran

Dans certains langages comme Java ou C ++, la barre oblique inverse doit être échappée. Donc, pour obtenir l'expression régulière "\.", Vous utiliseriez la chaîne "\\.". Python contourne ce problème en utilisant des chaînes brutes.
HackerBoss

Réponses:


258

TL; DR

Utilisez [.]au lieu de \.et [0-9]au lieu de \dpour éviter les problèmes d'échappement dans certains langages (comme Java).

Merci à celui sans nom pour avoir reconnu cela à l'origine.

Un modèle relativement simple pour faire correspondre un nombre à virgule flottante est

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

Cela correspondra:

  • 123
  • 123.456
  • .456

Voir un exemple de travail

Si vous souhaitez également faire correspondre 123.(un point sans partie décimale), vous aurez besoin d'une expression légèrement plus longue:

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

Voir la réponse de pkeller pour une explication plus complète de ce modèle

Si vous souhaitez inclure des nombres non décimaux, tels que hexadécimal et octal, consultez ma réponse à Comment puis-je identifier si une chaîne est un nombre? .

Si vous voulez valider qu'une entrée est un nombre (plutôt que de trouver un nombre dans l'entrée), vous devez entourer le motif avec ^et $, comme ceci:

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

Expressions régulières irrégulières

Les «expressions régulières», telles qu'implémentées dans la plupart des langages modernes, API, frameworks, bibliothèques, etc., sont basées sur un concept développé dans la théorie formelle du langage . Cependant, les ingénieurs logiciels ont ajouté de nombreuses extensions qui poussent ces implémentations bien au-delà de la définition formelle. Ainsi, alors que la plupart des moteurs d'expressions régulières se ressemblent, il n'y a en fait pas de norme. Pour cette raison, tout dépend du langage, de l'API, du framework ou de la bibliothèque que vous utilisez.

(Incidemment, pour aider à réduire la confusion, beaucoup ont commencé à utiliser « regex » ou « regexp » pour décrire ces langages de correspondance améliorés. Voir Une expression régulière est-elle identique à une expression régulière? Sur RexEgg.com pour plus d'informations.)

Cela dit, la plupart des moteurs de regex (en fait, tous, pour autant que je sache) accepteraient \.. Très probablement, il y a un problème avec l'échappement.

Le problème de la fuite

Certains langages ont un support intégré pour les expressions régulières, comme JavaScript . Pour les langues qui ne le font pas, s'échapper peut être un problème.

C'est parce que vous codez essentiellement dans une langue au sein d'une langue. Java, par exemple, utilise \comme caractère d'échappement dans ses chaînes, donc si vous voulez placer un caractère anti-slash littéral dans une chaîne, vous devez l'échapper:

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

Cependant, les expressions régulières utilisent également le \caractère pour s'échapper, donc si vous voulez faire correspondre un \caractère littéral , vous devez l'échapper pour le moteur d'expression régulière, puis l'échapper à nouveau pour Java:

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

Dans votre cas, vous n'avez probablement pas échappé à la barre oblique inverse dans le langage dans lequel vous programmez:

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

Toutes ces évasions peuvent devenir très déroutantes. Si le langage avec lequel vous travaillez prend en charge les chaînes brutes , vous devez les utiliser pour réduire le nombre de barres obliques inverses, mais tous les langages ne le font pas (notamment Java). Heureusement, il existe une alternative qui fonctionnera parfois:

String correctPattern = "[.]";

Pour un moteur regex, \.et cela [.]signifie exactement la même chose. Notez que cela ne fonctionne pas dans tous les cas, comme newline ( \\n), open square bracket ( \\[) et backslash ( \\\\ou [\\]).

Remarque sur la correspondance des nombres

(Indice: c'est plus difficile que vous ne le pensez)

Faire correspondre un nombre est une de ces choses que vous pensez être assez facile avec regex, mais c'est en fait assez délicat. Jetons un coup d'œil à votre approche, pièce par pièce:

[-+]?

Faites correspondre une option -ou+

[0-9]*

Correspond à 0 ou plusieurs chiffres séquentiels

\.?

Faites correspondre une option .

[0-9]*

Correspond à 0 ou plusieurs chiffres séquentiels

Tout d'abord, nous pouvons nettoyer un peu cette expression en utilisant un raccourci de classe de caractères pour les chiffres (notez que cela est également sensible au problème d'échappement mentionné ci-dessus):

[0-9] = \d

Je vais utiliser \dci-dessous, mais gardez à l'esprit que cela signifie la même chose que [0-9]. (Eh bien, en fait, dans certains moteurs \d, les chiffres de tous les scripts correspondront, donc cela correspondra plus que ce [0-9]ne sera, mais ce n'est probablement pas significatif dans votre cas.)

Maintenant, si vous regardez cela attentivement, vous vous rendrez compte que chaque partie de votre modèle est facultative . Ce modèle peut correspondre à une chaîne de longueur 0; une chaîne composée uniquement de +ou -; ou, une chaîne composée uniquement de a .. Ce n'est probablement pas ce que vous vouliez.

Pour résoudre ce problème, il est utile de commencer par «ancrer» votre expression régulière avec la chaîne requise le plus strict, probablement un seul chiffre:

\d+

Maintenant, nous voulons ajouter la partie décimale, mais elle ne va pas là où vous pensez qu'elle pourrait:

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

Cela correspondra toujours à des valeurs telles que 123.. Pire encore, il y a une teinte perverse à ce sujet. Le point est facultatif, ce qui signifie que vous avez deux classes répétées côte à côte ( \d+et \d*). Cela peut en fait être dangereux s'il est utilisé de la mauvaise manière, ouvrant votre système aux attaques DoS.

Pour résoudre ce problème, plutôt que de traiter le point comme facultatif, nous devons le traiter comme requis (pour séparer les classes de caractères répétées) et à la place rendre la partie décimale entière facultative:

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

Cela va mieux maintenant. Nous avons besoin d'un point entre la première séquence de chiffres et la seconde, mais il y a un défaut fatal: nous ne pouvons pas faire correspondre .123car un chiffre de tête est maintenant requis.

C'est en fait assez facile à résoudre. Au lieu de rendre la partie "décimale" du nombre facultative, nous devons la considérer comme une séquence de caractères: 1 ou plusieurs nombres qui peuvent être préfixés par un .qui peuvent être préfixés par 0 ou plusieurs nombres:

(\d*\.)?\d+

Maintenant, nous ajoutons simplement le signe:

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

Bien sûr, ces barres obliques sont assez ennuyeuses en Java, nous pouvons donc les remplacer dans nos classes de caractères de forme longue:

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

Matching versus validation

Cela a été mentionné dans les commentaires à quelques reprises, alors j'ajoute un addendum sur l'appariement et la validation.

Le but de la correspondance est de trouver du contenu dans l'entrée ("l'aiguille dans une botte de foin"). Le but de la validation est de s'assurer que l'entrée est dans un format attendu.

Les expressions régulières, de par leur nature, ne correspondent qu'à du texte. Compte tenu de certains commentaires, ils trouveront un texte correspondant ou ils ne le trouveront pas. Cependant, en "accrochant" une expression au début et à la fin de l'entrée avec des balises d'ancrage ( ^et $), nous pouvons nous assurer qu'aucune correspondance n'est trouvée à moins que l'entrée entière ne corresponde à l'expression, en utilisant efficacement des expressions régulières pour valider .

Le regex décrit ci-dessus ( [+-]?([0-9]*[.])?[0-9]+) correspondra à un ou plusieurs nombres dans une chaîne cible. Donc, compte tenu de l'entrée:

apple 1.34 pear 7.98 version 1.2.3.4

Le regex correspondra 1.34, 7.98, 1.2, .3et .4.

Pour valider qu'une entrée donnée est un nombre et rien d'autre qu'un nombre, «accrochez» l'expression au début et à la fin de l'entrée en l'enveloppant dans des balises d'ancrage:

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

Cela ne trouvera une correspondance que si l'entrée entière est un nombre à virgule flottante, et ne trouvera pas de correspondance si l'entrée contient des caractères supplémentaires. Donc, étant donné l'entrée 1.2, une correspondance sera trouvée, mais apple 1.2 pearaucune correspondance ne sera trouvée.

Notez que certains moteurs regex ont une fonction validate, isMatchou similaire, qui fait essentiellement ce que j'ai décrit automatiquement, retournant truesi une correspondance est trouvée et falsesi aucune correspondance n'est trouvée. Gardez également à l'esprit que certains moteurs vous permettent de définir des indicateurs qui modifient la définition de ^et $, correspondant au début / à la fin d'une ligne plutôt qu'au début / à la fin de l'entrée entière. Ce n'est généralement pas la valeur par défaut, mais soyez à l'affût de ces drapeaux.


2
JDB, merci et j'espère que vous êtes toujours là! Je lis votre message dans le futur :) Votre réponse prend certainement soin de 0.24 et 2.2 et interdit correctement 4.2.44 Tous testés avec regex101.com Cependant, cela interdit 123. ce qui, comme vous le dites, peut être acceptable (et je pense qu'il est!). Je peux résoudre ce problème en changeant votre expression en [- +]? (\ D * [.])? \ D * (notez * à la fin au lieu de +) mais alors des choses folles comme. (votre deuxième exemple) sont autorisés. Bref, avoir mon gâteau et le manger aussi?
Dave

2
@Dave -\d+(\.\d*)?|\.\d+
JDB se souvient encore de Monica le

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

1
@yeouuu oui, car 1.correspond. Ajoutez ^et $au début et à la fin de l'expression régulière si vous ne voulez faire correspondre que si l'entrée entière correspond.
JDB se souvient toujours de Monica le

5
les floats peuvent avoir des exposants ou être NaN / Inf, donc j'utiliserais ceci:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))e / d pour float / double precision float. N'oubliez pas un drapeau pliant à la regex
Markus Schmassmann

23

Je ne pense pas qu'aucune des réponses sur cette page au moment de la rédaction soit correcte (de nombreuses autres suggestions ailleurs sur SO sont également fausses). La complication est que vous devez correspondre à toutes les possibilités suivantes:

  • Pas de point décimal (c'est-à-dire une valeur entière)
  • Chiffres avant et après la virgule décimale (par exemple 0.35, 22.165)
  • Chiffres avant la virgule décimale uniquement (par exemple 0., 1234.)
  • Chiffres après la virgule décimale uniquement (par exemple .0, .5678)

Dans le même temps, vous devez vous assurer qu'il y a au moins un chiffre quelque part, c'est-à-dire que les éléments suivants ne sont pas autorisés:

  • un point décimal seul
  • une virgule décimale signée sans chiffres (c'est +.-à- dire ou -.)
  • +ou -seuls
  • une chaîne vide

Cela semble délicat au début, mais une façon de trouver l'inspiration est de regarder la source OpenJDK de la java.lang.Double.valueOf(String)méthode (commencer à http://hg.openjdk.java.net/jdk8/jdk8/jdk , cliquer sur "parcourir", naviguer vers le bas /src/share/classes/java/lang/et trouvez la Doubleclasse). Le long regex que cette classe contient répond à diverses possibilités que l'OP n'avait probablement pas à l'esprit, mais en ignorant pour simplifier les parties qui traitent de NaN, l'infini, la notation hexadécimale et les exposants, et en utilisant \dplutôt que la notation POSIX pour un seul chiffre, je peux réduire les parties importantes de l'expression régulière pour un nombre à virgule flottante signé sans exposant à:

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

Je ne pense pas qu'il existe un moyen d'éviter la (...)|(...)construction sans autoriser quelque chose qui ne contient pas de chiffres, ou d'interdire l'une des possibilités qui n'a pas de chiffres avant la virgule décimale ou pas de chiffres après.

De toute évidence, dans la pratique, vous devrez prendre en compte les espaces de fin ou précédents, soit dans l'expression régulière elle-même, soit dans le code qui l'utilise.


Si vous ajoutez l'exigence de faire correspondre des nombres comme 123., alors oui ... le commutateur ou est la seule solution, comme je l'ai souligné dans un commentaire sur mon message d'origine.
JDB se souvient encore de Monica

1
Ceci, et toutes / la plupart des autres réponses, ignorent qu'un flottant peut avoir un exposant.
NateS

1
@NateS C'est vrai, j'ai écrit "en ignorant pour simplifier les parties qui traitent de NaN, de l'infini, de la notation hexadécimale et des exposants", parce que cela semble correspondre à la portée de la question de l'OP. Il existe des implémentations plus complètes, y compris celle que j'ai trouvée dans le code source JDK.
pkeller

1
Le regex [+-]?((?=\.?\d)\d*\.?\d*)peut-il être utilisé pour éviter l'alternance? It uses a lookahead ...
4esn0k

1
@ 4esn0k Belle regex! J'ai joué avec, et ça marche. J'ai deux mises en garde: (1) tous les moteurs regex ne prennent pas en charge les assertions de largeur nulle (bien que la plupart des moteurs modernes le fassent, AFAIK), et (2) le look-ahead n'est qu'une alternative par un autre nom: le moteur doit encore essayer quelque chose et revenir en arrière si cela ne fonctionne pas. Ayez néanmoins un vote positif pour une idée très soignée.
pkeller

7

ce dont vous avez besoin est:

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

J'ai échappé aux signes «+» et «-» et j'ai également regroupé la décimale avec ses chiffres suivants depuis quelque chose comme «1». n'est pas un nombre valide.

Les modifications vous permettront de faire correspondre les entiers et les flottants. par exemple:

0
+1
-2.0
2.23442

Le problème avec cette expression est qu'elle .1ne serait pas autorisée, même si une telle entrée est universellement reconnue comme correcte.
JDB se souvient encore de Monica le

Cela acceptera désormais les chaînes de longueur nulle -et +, qui ne sont pas des nombres. Regex est délicat! :)
JDB se souvient encore de Monica le

De plus, cela ne répond pas à la question réelle du PO, c'est-à-dire que \.cela ne fonctionne pas.
JDB se souvient encore de Monica le

7

Je veux faire correspondre ce que la plupart des langues considèrent comme des nombres valides (entiers et flottants):

  • '5' / '-5'

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

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

Remarques:

  • 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

Afin de soutenir à la fois «1». et '.1' nous avons besoin d'un opérateur OR ('|') pour être sûr d'exclure '.' de l'appariement.

[+-]?+/- chanter est facultatif car ?signifie 0 ou 1 correspondances

( puisque nous avons 2 sous-expressions, nous devons les mettre entre parenthèses

\d+([.]\d*)?(e[+-]?\d+)? Ceci est pour les nombres commençant par un chiffre

| sépare les sous-expressions

[.]\d+(e[+-]?\d+)? c'est pour les nombres commençant par "."

) fin d'expressions

  • Pour les nombres commençant par "."

[.] le premier caractère est un point (entre crochets ou bien c'est un caractère générique)

\d+ un ou plusieurs chiffres

(e[+-]?\d+)? il s'agit d'une notation scientifique facultative (0 ou 1 correspond à la fin de '?')

  • Pour les nombres commençant par un chiffre

\d+ un ou plusieurs chiffres

([.]\d*)? facultativement, nous pouvons avoir un caractère point après zéro ou plusieurs chiffres

(e[+-]?\d+)? ceci est une notation scientifique facultative

  • Notation scientifique

e littéral qui spécifie l'exposant

[+-]? signe d'exposant facultatif

\d+ un ou plusieurs chiffres

Tous ces éléments combinés:

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

A accepter Eaussi:

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

( Cas de test )


4

C'est simple: vous avez utilisé Java et vous devriez utiliser à la \\.place de \.(recherche de caractères s'échappant en Java).


Vous avez probablement raison ... le message d'erreur ressemble à une erreur de syntaxe du langage de programmation plutôt qu'à une erreur d'analyseur regex.
JDB se souvient encore de Monica

3

Celui-ci a fonctionné pour moi:

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

Vous pouvez également utiliser celui-ci (sans paramètre nommé):

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

Utilisez un testeur de regex en ligne pour le tester (par exemple, regex101)


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

Cela correspondra:

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

Bien que cet extrait de code soit le bienvenu et puisse fournir une aide, il serait grandement amélioré s'il incluait une explication sur la manière et la raison pour laquelle cela résout le problème. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant! Veuillez modifier votre réponse pour ajouter une explication et donner une indication des limites et des hypothèses applicables.
Toby Speight

Oh thnks, je suis à la recherche de ça
Serg Burlaka

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

[+-]? - signe de tête facultatif

(([1-9][0-9]*)|(0)) - entier sans zéro non significatif, y compris un seul zéro

([.,][0-9]+)? - partie fractionnaire facultative


1
Donnez plus d'informations - pour les personnes ne connaissant pas les expressions rationnelles, il s'agit d'hyerogliphs. Pour les gens qui les connaissent, ils n'en ont pas besoin.
peterh - Réintégrer Monica

0

En C ++ en utilisant la bibliothèque regex

La réponse se passerait comme ceci:

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

Notez que je ne prends pas le symbole du signe, si vous le vouliez avec le symbole du signe, cela se passerait:

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

Cela sépare également un nombre régulier ou un nombre décimal.


0

En notation C, le nombre flottant peut se présenter sous les formes suivantes:

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

Pour créer une expression régulière float, je vais d'abord créer "int regular expresion variable":

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

Maintenant, j'écrirai de petits morceaux d'expression régulière de float - la solution est de concaténer ces morceaux avec ou simbol "|".

Morceaux:

- (([+-]?{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

Solution finale (concanant de petits morceaux):

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


-1

pour javascript

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

Ce qui fonctionnerait pour 1,23 1234,22 0 0,12 12

Vous pouvez modifier les parties dans le {}pour obtenir des résultats différents en longueur décimale et devant la décimale également. Ceci est utilisé dans les entrées pour entrer le nombre et vérifier chaque entrée lorsque vous tapez, autorisant uniquement ce qui passe.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.