Expresión regular para números de coma flotante

115

Tengo una tarea para hacer coincidir números de punto flotante. He escrito la siguiente expresión regular para ello:

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

Pero devuelve un error:

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

Según mi conocimiento, necesitamos usar un carácter de escape para .también. Por favor, corríjame donde me equivoque.

Gopal Samant
fuente
10
¿En qué idioma se usa esta expresión regular?
CaffGeek
3
@JDB - ¿Por qué estás regalando 100 puntos por una expresión regular de número / flotante? El estándar siempre ha sido (?:\d+(?:\.\d*)?|\.\d+)y se ha publicado ad infinitum en SO ...
1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?si también desea captar la notación exponencial, e, g, 3.023e-23
wcochran
En algunos lenguajes como Java o C ++, la barra invertida debe escaparse. Entonces, para obtener la expresión regular "\.", Usaría la cadena "\\.". Python soluciona esto mediante el uso de cadenas sin formato.
HackerBoss

Respuestas:

258

TL; DR

Úselo en [.]lugar de \.y en [0-9]lugar de \dpara evitar problemas de escape en algunos lenguajes (como Java).

Gracias al sin nombre por reconocer esto originalmente.

Un patrón relativamente simple para hacer coincidir un número de punto flotante es

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

Esto coincidirá con:

  • 123
  • 123.456
  • .456

Ver un ejemplo funcional

Si también desea hacer coincidir 123.(un período sin parte decimal), necesitará una expresión un poco más larga:

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

Vea la respuesta de pkeller para una explicación más completa de este patrón

Si desea incluir números no decimales, como hexadecimal y octal, consulte mi respuesta a ¿Cómo identifico si una cadena es un número? .

Si desea validar que una entrada es un número (en lugar de encontrar un número dentro de la entrada), entonces debe rodear el patrón con ^y $, así:

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

Expresiones regulares irregulares

Las "expresiones regulares", tal como se implementan en la mayoría de los lenguajes, API, frameworks, bibliotecas, etc., se basan en un concepto desarrollado en la teoría del lenguaje formal . Sin embargo, los ingenieros de software han agregado muchas extensiones que llevan estas implementaciones mucho más allá de la definición formal. Entonces, aunque la mayoría de los motores de expresión regular se parecen entre sí, en realidad no existe un estándar. Por esta razón, mucho depende del lenguaje, API, marco o biblioteca que esté utilizando.

(Por cierto, para ayudar a reducir la confusión, muchos han comenzado a usar " regex " o " regexp " para describir estos idiomas de coincidencia mejorados. Consulte ¿Es una expresión regular lo mismo que una expresión regular? En RexEgg.com para obtener más información).

Dicho esto, la mayoría de los motores de expresiones regulares (en realidad, todos, hasta donde yo sé) aceptarían \.. Lo más probable es que haya un problema para escapar.

El problema de escapar

Algunos lenguajes tienen soporte integrado para expresiones regulares, como JavaScript . Para aquellos idiomas que no lo hacen, escapar puede ser un problema.

Esto se debe a que básicamente está codificando en un idioma dentro de otro idioma. Java, por ejemplo, se usa \como carácter de escape dentro de sus cadenas, por lo que si desea colocar un carácter de barra invertida literal dentro de una cadena, debe escapar de él:

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

Sin embargo, las expresiones regulares también usan el \carácter para escapar, por lo que si desea hacer coincidir un \carácter literal , debe escaparlo para el motor de expresiones regulares y luego escaparlo nuevamente para Java:

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

En su caso, probablemente no haya escapado del carácter de barra invertida en el lenguaje en el que está programando:

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

Todo este escape puede resultar muy confuso. Si el lenguaje con el que está trabajando admite cadenas sin formato , entonces debería usarlas para reducir el número de barras invertidas, pero no todos los lenguajes lo hacen (más notablemente: Java). Afortunadamente, existe una alternativa que funcionará algunas veces:

String correctPattern = "[.]";

Para un motor de expresiones regulares, \.y [.]significa exactamente lo mismo. Tenga en cuenta que esto no funciona en todos los casos, como nueva línea ( \\n), corchete abierto ( \\[) y barra invertida ( \\\\o [\\]).

Una nota sobre la coincidencia de números

(Pista: es más difícil de lo que piensas)

Hacer coincidir un número es una de esas cosas que pensaría que es bastante fácil con expresiones regulares, pero en realidad es bastante complicado. Echemos un vistazo a su enfoque, pieza por pieza:

[-+]?

Coincide con un opcional -o+

[0-9]*

Coincide con 0 o más dígitos secuenciales

\.?

Coincide con un opcional .

[0-9]*

Coincide con 0 o más dígitos secuenciales

Primero, podemos limpiar un poco esta expresión usando una abreviatura de clase de caracteres para los dígitos (tenga en cuenta que esto también es susceptible al problema de escape mencionado anteriormente):

[0-9] = \d

Voy a usar a \dcontinuación, pero ten en cuenta que significa lo mismo que [0-9]. (Bueno, en realidad, en algunos motores \dcoincidirán los dígitos de todos los scripts, por lo que coincidirá con más de [0-9]lo que lo hará, pero eso probablemente no sea significativo en su caso).

Ahora, si observa esto detenidamente, se dará cuenta de que cada parte de su patrón es opcional . Este patrón puede coincidir con una cadena de longitud 0; una cadena compuesta solo por +o -; o, una cadena compuesta solo por a .. Probablemente esto no sea lo que pretendías.

Para solucionar esto, es útil comenzar por "anclar" su expresión regular con la cadena mínima requerida, probablemente un solo dígito:

\d+

Ahora queremos agregar la parte decimal, pero no va donde cree que podría:

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

Esto seguirá coincidiendo con valores como 123.. Peor aún, tiene un matiz de maldad . El período es opcional, lo que significa que tiene dos clases repetidas una al lado de la otra ( \d+y \d*). En realidad, esto puede ser peligroso si se usa de manera incorrecta, lo que abre su sistema a ataques DoS.

Para solucionar este problema, en lugar de tratar el período como opcional, debemos tratarlo como se requiere (para separar las clases de caracteres repetidas) y, en su lugar, hacer que toda la parte decimal sea opcional:

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

Esto se ve mejor ahora. Requerimos un período entre la primera secuencia de dígitos y el segundo, pero hay un defecto fatal: no podemos coincidir .123porque ahora se requiere un dígito inicial.

En realidad, esto es bastante fácil de solucionar. En lugar de hacer que la parte "decimal" del número sea opcional, debemos considerarla como una secuencia de caracteres: 1 o más números que pueden tener como prefijo un .prefijo con 0 o más números:

(\d*\.)?\d+

Ahora solo agregamos el signo:

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

Por supuesto, esas barras son bastante molestas en Java, por lo que podemos sustituirlas en nuestras clases de caracteres de formato largo:

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

Coincidencia versus validación

Esto ha aparecido en los comentarios un par de veces, así que estoy agregando un apéndice sobre coincidencia versus validación.

El objetivo de hacer coincidir es encontrar algún contenido dentro de la entrada (la "aguja en un pajar"). El objetivo de la validación es garantizar que la entrada tenga el formato esperado.

Las expresiones regulares, por su naturaleza, solo coinciden con el texto. Dada alguna entrada, encontrarán algún texto coincidente o no. Sin embargo, al "ajustar" una expresión al principio y al final de la entrada con etiquetas de anclaje ( ^y $), podemos asegurarnos de que no se encuentre ninguna coincidencia a menos que toda la entrada coincida con la expresión, utilizando de manera efectiva expresiones regulares para validar .

La expresión regular descrita anteriormente ( [+-]?([0-9]*[.])?[0-9]+) coincidirá con uno o más números dentro de una cadena de destino. Entonces, dada la entrada:

apple 1.34 pear 7.98 version 1.2.3.4

La expresión regular coincidirá con 1.34, 7.98, 1.2, .3y .4.

Para validar que una entrada dada es un número y nada más que un número, "ajuste" la expresión al inicio y al final de la entrada envolviéndola en etiquetas de anclaje:

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

Esto solo encontrará una coincidencia si toda la entrada es un número de punto flotante y no encontrará una coincidencia si la entrada contiene caracteres adicionales. Entonces, dada la entrada 1.2, se encontrará una coincidencia, pero apple 1.2 pearno se encontrará ninguna coincidencia.

Tenga en cuenta que algunos motores de expresiones regulares tienen una función validate, isMatcho similar, que esencialmente hace lo que he descrito automáticamente, devolviendo truesi se encuentra una coincidencia y falsesi no se encuentra ninguna coincidencia. También tenga en cuenta que algunos motores le permiten establecer indicadores que cambian la definición de ^y $, haciendo coincidir el principio / final de una línea en lugar del principio / final de toda la entrada. Por lo general, este no es el predeterminado, pero esté atento a estas banderas.

JDB todavía recuerda a Monica
fuente
2
JDB, gracias y espero que sigas por aquí. Estoy leyendo tu publicación en el futuro :) Tu respuesta ciertamente se ocupa de 0.24 y 2.2 y rechaza correctamente 4.2.44 Todo probado con regex101.com Sin embargo, rechaza 123. que, como dices, puede ser aceptable (y creo que ¡es!). Puedo arreglar esto cambiando su expresión a [- +]? (\ D * [.])? \ D * (observe * al final en lugar de +) pero luego cosas locas como. (su segundo ejemplo) están permitidos. De todos modos, ¿tener mi pastel y comérmelo también?
Dave
2
@Dave -\d+(\.\d*)?|\.\d+
JDB todavía recuerda a Monica
/[-+]?(\d*[.])?\d+/.test("1.bc") // returns true
yeouuu
1
@yeouuu sí, porque 1.coincide. Agregue ^y $al principio y al final de la expresión regular si desea hacer coincidir solo si toda la entrada coincide.
JDB todavía recuerda a Monica
5
los flotadores pueden tener exponentes o ser NaN / Inf, por lo que usaría esto:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))e / d para float / double precisión float. No olvide una bandera de caso plegable a la expresión regular
Markus Schmassmann
23

No creo que ninguna de las respuestas en esta página en el momento de escribir este artículo sea correcta (también muchas otras sugerencias en otros lugares de SO también están equivocadas). La complicación es que debe combinar todas las siguientes posibilidades:

  • Sin punto decimal (es decir, un valor entero)
  • Dígitos antes y después del punto decimal (p 0.35. Ej. ,22.165 )
  • Dígitos antes del punto decimal solamente (p 0.. Ej. ,1234. )
  • Dígitos después del punto decimal solamente (p .0. Ej. , .5678)

Al mismo tiempo, debe asegurarse de que haya al menos un dígito en alguna parte, es decir, no se permiten los siguientes:

  • un punto decimal por sí solo
  • un punto decimal con signo sin dígitos (es decir +. o -.)
  • + o - por su cuenta
  • una cuerda vacía

Esto parece complicado al principio, pero una forma de encontrar inspiración es buscar el java.lang.Double.valueOf(String)método en la fuente OpenJDK (comience en http://hg.openjdk.java.net/jdk8/jdk8/jdk , haga clic en "navegar", navegue hacia abajo /src/share/classes/java/lang/y encuentra la Doubleclase). La expresión regular larga que contiene esta clase cubre varias posibilidades que el OP probablemente no tenía en mente, pero ignorando por simplicidad las partes que tratan con NaN, infinito, notación hexadecimal y exponentes, y usando en \dlugar de la notación POSIX para un solo dígito, puedo reducir las partes importantes de la expresión regular para un número de punto flotante firmado sin exponente a:

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

No creo que haya una forma de evitar la (...)|(...)construcción sin permitir algo que no contenga dígitos, o prohibir una de las posibilidades que no tenga dígitos antes del punto decimal o sin dígitos después.

Obviamente, en la práctica, deberá tener en cuenta los espacios en blanco finales o anteriores, ya sea en la expresión regular o en el código que la usa.

pkeller
fuente
Si agrega el requisito de hacer coincidir números como 123., entonces sí ... el interruptor o es la única solución, como señalé en un comentario en mi publicación original.
JDB todavía recuerda a Monica el
1
Esta, y todas / la mayoría de las otras respuestas, ignoran que un flotante puede tener un exponente.
NateS
1
@NateS Así es, escribí "ignorando por simplicidad las partes que tratan con NaN, infinito, notación hexadecimal y exponentes", porque eso parece coincidir con el alcance de la pregunta del OP. Hay implementaciones más completas, incluida la que encontré en el código fuente de JDK.
pkeller
1
¿Se [+-]?((?=\.?\d)\d*\.?\d*)puede usar la expresión regular para evitar la alternancia? Utiliza un lookahead ...
4esn0k
1
@ 4esn0k ¡Buena expresión regular! He jugado con él y funciona. Tengo dos advertencias: (1) no todos los motores de expresiones regulares admiten afirmaciones de ancho cero (aunque la mayoría de los modernos lo hacen, AFAIK), y (2) la anticipación es solo una alternancia con otro nombre: el motor todavía tiene que probar algo y retroceda si no funciona. Sin embargo, tenga un voto positivo para una idea muy buena.
pkeller
7

lo que necesitas es:

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

Me escapé del signo "+" y "-" y también agrupé el decimal con sus siguientes dígitos desde algo como "1". No es un número válido.

Los cambios le permitirán hacer coincidir enteros y flotantes. por ejemplo:

0
+1
-2.0
2.23442
DiverseAndRemote.com
fuente
El problema con esta expresión es que .1no se permitiría, aunque tal entrada se reconoce universalmente como correcta.
JDB todavía recuerda a Monica el
Esto ahora aceptará cadenas de longitud cero -y +, que no son números. ¡Regex es complicado! :)
JDB todavía recuerda a Monica
Además, esto no responde a la pregunta real del OP, que es que \.no funciona.
JDB todavía recuerda a Monica el
7

Quiero hacer coincidir lo que la mayoría de los idiomas consideran números válidos (enteros y flotantes):

  • '5' / '-5'

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

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

Notas:

  • 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

Para admitir tanto '1'. y '.1' necesitamos un operador OR ('|') para asegurarnos de excluir '.' de emparejar.

[+-]? +/- cantar es opcional ya que ? significa 0 o 1 coincidencias

( como tenemos 2 subexpresiones, debemos ponerlas entre paréntesis

\d+([.]\d*)?(e[+-]?\d+)? Esto es para números que comienzan con un dígito

| separa subexpresiones

[.]\d+(e[+-]?\d+)? esto es para números que comienzan con '.'

) fin de expresiones

  • Para números que comienzan con '.'

[.] el primer carácter es un punto (entre corchetes o de lo contrario es un carácter comodín)

\d+ uno o más dígitos

(e[+-]?\d+)? esta es una notación científica opcional (0 o 1 coincidencias debido a la terminación '?')

  • Para números que comienzan con un dígito

\d+ uno o más dígitos

([.]\d*)? opcionalmente podemos tener un carácter de punto y cero o más dígitos después de él

(e[+-]?\d+)? esta es una notación científica opcional

  • Notación cientifica

e literal que especifica exponente

[+-]? signo de exponente opcional

\d+ uno o más dígitos

Todos los combinados:

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

Para aceptar Etambién:

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

( Casos de prueba )

Yannis T
fuente
4

Esto es simple: ha usado Java y debería usar en \\.lugar de \.(buscar caracteres que se escapan en Java).

el sin nombre
fuente
Probablemente tenga razón ... el mensaje de error parece un error de sintaxis del lenguaje de programación en lugar de un error del analizador de expresiones regulares.
JDB todavía recuerda a Monica el
3

Este funcionó para mí:

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

También puede usar este (sin parámetro con nombre):

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

Use algún probador de expresiones regulares en línea para probarlo (por ejemplo, regex101)

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

Esto coincidirá con:

  1. 1.2
  2. 12,3
  3. 1,2
  4. 12,3
Mihai Ciobanu
fuente
Si bien este fragmento de código es bienvenido y puede proporcionar algo de ayuda, sería mucho mejor si incluyera una explicación de cómo y por qué resuelve el problema. Recuerde que está respondiendo la pregunta para los lectores en el futuro, ¡no solo la persona que pregunta ahora! Por favor, editar su respuesta para agregar explicación y dar una indicación de lo que se aplican limitaciones y supuestos.
Toby Speight
oh gracias, estoy buscando esto
Serg Burlaka
0
[+-]?(([1-9][0-9]*)|(0))([.,][0-9]+)?

[+-]? - señal inicial opcional

(([1-9][0-9]*)|(0)) - entero sin cero a la izquierda, incluido un solo cero

([.,][0-9]+)? - parte fraccionaria opcional

Aleksei Gutikov
fuente
1
Proporcione más información: para las personas que no conocen las expresiones regulares, es hyerogliphs. Para las personas que los conocen, no lo necesitan.
Peterh - Reincorpora a Monica
0

En C ++ usando la biblioteca regex

La respuesta sería así:

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

Tenga en cuenta que no tomo el símbolo del signo, si lo quisiera con el símbolo del signo, se trataría de esto:

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

Esto también separa un número regular o un número decimal.

LuisDev99
fuente
0

En notación c, el número flotante puede aparecer en las siguientes formas:

  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 * 10 000

Para crear una expresión regular flotante, primero crearé una "variable de expresión regular int":

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

Ahora, escribiré pequeños trozos de expresión regular flotante; la solución es concatenar esos trozos con o simbol "|".

Trozos:

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

Solución final (concanar pequeños trozos):

(([+-]?{int})|(([+-]?{int})"."[0-9]*)|("."[0-9]*)|([+-]?{int}[eE][+-]?{int})
Zoran Medojević
fuente
-1
[+/-] [0-9]*.[0-9]+

Prueba esta solución.

Lola Gorochana
fuente
-1

para javascript

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

Cuál funcionaría para 1.23 1234.22 0 0.12 12

Puede cambiar las partes en {}para obtener diferentes resultados en la longitud decimal y también en el frente del decimal. Esto se usa en entradas para ingresar un número y verificar cada entrada mientras escribe, permitiendo solo lo que pasa.

mjwrazor
fuente