Expressão regular para números de ponto flutuante

115

Eu tenho uma tarefa para combinar números de ponto flutuante. Escrevi a seguinte expressão regular para ele:

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

Mas, ele retorna um erro:

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

De acordo com meu conhecimento, precisamos usar um caractere de escape para o .também. Por favor, corrija-me onde estou errado.

Gopal Samant
fonte
10
Em que idioma esta regex é usada?
CaffGeek
3
@JDB - Por que você está dando 100 pontos para uma regex número / float? O padrão sempre foi (?:\d+(?:\.\d*)?|\.\d+)e tem sido postado ad infinitum no SO ...
1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?se você deseja capturar a notação exponencial também, por exemplo, 3.023e-23
wcochran
Em algumas linguagens como Java ou C ++, a barra invertida deve ser escapada. Portanto, para obter a regex "\.", Você usaria a string "\\.". Python contorna isso usando strings brutas.
HackerBoss

Respostas:

258

TL; DR

Use em [.]vez de \.e em [0-9]vez de \dpara evitar problemas de escape em algumas linguagens (como Java).

Obrigado ao anônimo por originalmente reconhecer isso.

Um padrão relativamente simples para combinar um número de ponto flutuante é

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

Isso vai corresponder a:

  • 123
  • 123.456
  • .456

Veja um exemplo funcional

Se você também quiser fazer a correspondência 123.(um ponto sem parte decimal), precisará de uma expressão um pouco mais longa:

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

Veja a resposta de pkeller para uma explicação mais completa deste padrão

Se você deseja incluir números não decimais, como hexadecimal e octal, consulte minha resposta a Como posso identificar se uma string é um número? .

Se você deseja validar que uma entrada é um número (em vez de encontrar um número na entrada), você deve cercar o padrão com ^e $, assim:

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

Expressões regulares irregulares

As "expressões regulares", conforme implementadas na maioria das linguagens modernas, APIs, frameworks, bibliotecas, etc., são baseadas em um conceito desenvolvido na teoria da linguagem formal . No entanto, os engenheiros de software adicionaram muitas extensões que levam essas implementações muito além da definição formal. Portanto, embora a maioria dos mecanismos de expressão regular sejam semelhantes, na verdade não existe um padrão. Por isso, depende muito de qual linguagem, API, framework ou biblioteca você está usando.

(A propósito, para ajudar a reduzir a confusão, muitos passaram a usar " regex " ou " regexp " para descrever essas linguagens de correspondência aprimoradas. Consulte Regex é o mesmo que uma expressão regular? Em RexEgg.com para obter mais informações.)

Dito isso, a maioria dos motores de regex (na verdade, todos eles, pelo que eu sei) aceitariam \.. Provavelmente, há um problema com o escape.

O problema de escapar

Algumas linguagens têm suporte integrado para regexes, como JavaScript . Para aquelas linguagens que não o fazem, o escape pode ser um problema.

Isso ocorre porque você basicamente está codificando em um idioma dentro de outro idioma. Java, por exemplo, usa \como um caractere de escape dentro de suas strings, então se você quiser colocar um caractere literal de barra invertida em uma string, você deve escapar dele:

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

No entanto, regexes também usam o \caractere para escape, portanto, se você quiser corresponder a um \caractere literal , deverá escapar dele para o mecanismo de regexe e, em seguida, escapar novamente para Java:

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

No seu caso, você provavelmente não escapou do caractere de barra invertida na linguagem em que está programando:

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

Toda essa fuga pode ser muito confusa. Se a linguagem com a qual você está trabalhando suporta strings brutas , então você deve usá-las para reduzir o número de barras invertidas, mas nem todas as linguagens suportam (mais notavelmente: Java). Felizmente, há uma alternativa que funcionará algumas vezes:

String correctPattern = "[.]";

Para um motor regex, \.e [.]significa exatamente a mesma coisa. Observe que isso não funciona em todos os casos, como nova linha ( \\n), colchete de abertura ( \\[) e barra invertida ( \\\\ou [\\]).

Uma nota sobre números correspondentes

(Dica: é mais difícil do que você pensa)

Combinar um número é uma daquelas coisas que você acha que é muito fácil com regex, mas na verdade é bem complicado. Vamos dar uma olhada em sua abordagem, peça por peça:

[-+]?

Combine um opcional -ou+

[0-9]*

Corresponde a 0 ou mais dígitos sequenciais

\.?

Combine um opcional .

[0-9]*

Corresponde a 0 ou mais dígitos sequenciais

Primeiro, podemos limpar essa expressão um pouco usando uma abreviação de classe de caractere para os dígitos (observe que isso também é suscetível ao problema de escape mencionado acima):

[0-9] = \d

Vou usar \dabaixo, mas lembre-se de que significa a mesma coisa que [0-9]. (Bem, na verdade, em alguns mecanismos \dcorresponderá dígitos de todos os scripts, portanto, corresponderá mais do que [0-9], mas isso provavelmente não é significativo no seu caso.)

Agora, se você observar isso com atenção, perceberá que cada parte do seu padrão é opcional . Esse padrão pode corresponder a uma string de comprimento 0; uma corda composta apenas por +ou -; ou, uma corda composta apenas por a .. Provavelmente não é o que você pretendia.

Para corrigir isso, é útil começar "ancorando" sua regex com a string mínima necessária, provavelmente um único dígito:

\d+

Agora queremos adicionar a parte decimal, mas ela não vai para onde você pensa que poderia:

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

Isso ainda corresponderá a valores como 123.. Pior, tem um toque de maldade nisso. O período é opcional, o que significa que você tem duas classes repetidas lado a lado ( \d+e \d*). Na verdade, isso pode ser perigoso se usado da maneira errada, deixando seu sistema vulnerável a ataques DoS.

Para corrigir isso, em vez de tratar o período como opcional, precisamos tratá-lo como obrigatório (para separar as classes de caracteres repetidos) e, em vez disso, tornar opcional toda a parte decimal:

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

Isso está parecendo melhor agora. Exigimos um período entre a primeira sequência de dígitos e a segunda, mas há uma falha fatal: não podemos fazer a correspondência .123porque um dígito inicial agora é necessário.

Na verdade, isso é muito fácil de consertar. Em vez de tornar opcional a parte "decimal" do número, precisamos olhar para ela como uma sequência de caracteres: 1 ou mais números que podem ser prefixados por um .que pode ser prefixado por 0 ou mais números:

(\d*\.)?\d+

Agora apenas adicionamos o sinal:

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

Claro, essas barras são muito irritantes em Java, então podemos substituí-las em nossas classes de caracteres de formato longo:

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

Correspondência versus validação

Isso já apareceu nos comentários algumas vezes, então estou adicionando um adendo sobre correspondência versus validação.

O objetivo da correspondência é encontrar algum conteúdo na entrada (a "agulha em um palheiro"). O objetivo da validação é garantir que a entrada esteja em um formato esperado.

Regexes, por sua natureza, só correspondem a texto. Com alguma entrada, eles encontrarão algum texto correspondente ou não. No entanto, ao "encaixar" uma expressão no início e no final da entrada com marcas âncora ( ^e $), podemos garantir que nenhuma correspondência seja encontrada a menos que toda a entrada corresponda à expressão, usando efetivamente regexes para validar .

A regex descrita acima ( [+-]?([0-9]*[.])?[0-9]+) corresponderá a um ou mais números em uma string de destino. Então, dada a entrada:

apple 1.34 pear 7.98 version 1.2.3.4

A regex irá corresponder 1.34, 7.98, 1.2, .3e .4.

Para validar que uma determinada entrada é um número e nada além de um número, "encaixe" a expressão no início e no final da entrada envolvendo-a em tags âncora:

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

Isso só encontrará uma correspondência se a entrada inteira for um número de ponto flutuante e não encontrará uma correspondência se a entrada contiver caracteres adicionais. Portanto, dada a entrada 1.2, uma correspondência será encontrada, mas apple 1.2 pearnenhuma correspondência será encontrada.

Observe que alguns motores de regex têm uma função validate, isMatchou semelhante, que essencialmente faz o que descrevi automaticamente, retornando truese uma correspondência for encontrada e falsese nenhuma correspondência for encontrada. Também tenha em mente que alguns mecanismos permitem que você defina sinalizadores que alteram a definição de ^e $, correspondendo ao início / fim de uma linha ao invés do início / fim de toda a entrada. Normalmente, esse não é o padrão, mas fique atento a esses sinalizadores.

JDB ainda se lembra da Monica
fonte
2
JDB, obrigado e espero que você ainda esteja por aí! Estou lendo sua postagem no futuro :) Sua resposta certamente considera 0,24 e 2,2 e desautoriza corretamente 4.2.44 Todos testados com regex101.com No entanto, desautoriza 123. que, como você diz, pode ser aceitável (e eu acho isso é!). Posso corrigir isso alterando sua expressão para [- +]? (\ D * [.])? \ D * (observe * no final em vez de +), mas então coisas malucas como. (seu segundo exemplo) são permitidos. Afinal, ter meu bolo e comê-lo também?
Dave
2
@Dave -\d+(\.\d*)?|\.\d+
JDB ainda se lembra de Monica,
/[-+]?(\d*[.])?\d+/.test("1.bc") // returns true
yeouuu
1
@yeouuu sim, porque 1.corresponde. Adicione ^e $ao início e ao final da regex se desejar corresponder apenas se toda a entrada corresponder.
JDB ainda se lembra de Monica
5
floats podem ter expoentes ou ser NaN / Inf, então eu usaria este:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))e / d para float / float de precisão dupla. Não se esqueça de um sinalizador de caixa dobrável para a regex
Markus Schmassmann
23

Eu não acho que nenhuma das respostas nesta página no momento da escrita esteja correta (muitas outras sugestões em outras partes do SO também estão erradas). A complicação é que você precisa combinar todas as seguintes possibilidades:

  • Sem ponto decimal (ou seja, um valor inteiro)
  • Dígitos antes e depois do ponto decimal (por exemplo 0.35, 22.165)
  • Dígitos antes do ponto decimal apenas (por exemplo 0., 1234.)
  • Dígitos após o ponto decimal apenas (por exemplo .0, .5678)

Ao mesmo tempo, você deve garantir que haja pelo menos um dígito em algum lugar, ou seja, o seguinte não é permitido:

  • um ponto decimal por conta própria
  • um ponto decimal com sinais sem dígitos ( +.ou seja, ou -.)
  • +ou -por conta própria
  • uma string vazia

Isso parece complicado no início, mas uma maneira de encontrar inspiração é olhar o código-fonte do OpenJDK para o java.lang.Double.valueOf(String)método (comece em http://hg.openjdk.java.net/jdk8/jdk8/jdk , clique em "navegar", navegue para baixo /src/share/classes/java/lang/e encontrar a Doubleclasse). A longa regex que esta classe contém atende a várias possibilidades que o OP provavelmente não tinha em mente, mas ignorando para simplificar as partes que lidam com NaN, infinito, notação hexadecimal e expoentes, e usando \dem vez da notação POSIX para um único dígito, posso reduzir as partes importantes da regex para um número de ponto flutuante assinado sem expoente para:

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

Não creio que haja como evitar a (...)|(...)construção sem permitir algo que não contenha algarismos, ou proibir uma das possibilidades que não tem algarismos antes da vírgula ou nenhum algarismo depois dela.

Obviamente, na prática, você precisará fornecer espaços em branco à direita ou precedentes, no próprio regex ou no código que o utiliza.

pkeller
fonte
Se você adicionar o requisito de correspondência de números como 123., então sim ... a opção ou é a única solução, como indiquei em um comentário em meu post original.
JDB ainda se lembra de Monica
1
Esta e a maioria das outras respostas ignoram que um float pode ter um expoente.
NateS
1
@NateS Isso mesmo, eu escrevi "ignorando para simplificar as partes que lidam com NaN, infinito, notação hexadecimal e expoentes", porque isso parece corresponder ao escopo da pergunta do OP. Existem implementações mais completas, incluindo aquela que encontrei no código-fonte do JDK.
pkeller
1
A regex pode [+-]?((?=\.?\d)\d*\.?\d*)ser usada para evitar a alternância? Ele usa um lookahead ...
4esn0k
1
@ 4esn0k Bom regex! Eu brinquei com isso e funciona. Eu tenho duas advertências: (1) nem todos os motores de regex suportam asserções de largura zero (embora a maioria dos modernos o façam, AFAIK) e (2) a antecipação é apenas uma alternância por outro nome: o motor ainda precisa tentar algo e retroceda se não funcionar. Mesmo assim, tenha um voto positivo para uma ideia muito bacana.
pkeller
7

o que você precisa é:

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

Eu escapei do sinal "+" e "-" e também agrupei o decimal com seus dígitos seguintes, já que algo como "1". não é um número válido.

As mudanças permitirão que você combine inteiros e flutuantes. por exemplo:

0
+1
-2.0
2.23442
DiverseAndRemote.com
fonte
O problema com esta expressão é que .1não seria permitida, embora tal entrada seja universalmente reconhecida como correta.
JDB ainda se lembra de Monica
Isso agora aceitará strings de comprimento zero -e +, que não são números. Regex é complicado! :)
JDB ainda se lembra de Monica
Além disso, isso não responde à pergunta real do OP, que \.não funciona.
JDB ainda se lembra de Monica
7

Eu quero combinar o que a maioria das línguas considera números válidos (inteiros e flutuantes):

  • '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 oferecer suporte a '1' e '.1' precisamos de um operador OR ('|') para garantir a exclusão de '.' de combinar.

[+-]?+/- cantar é opcional, pois ?significa 0 ou 1 correspondência

( uma vez que temos 2 subexpressões, precisamos colocá-las entre parênteses

\d+([.]\d*)?(e[+-]?\d+)? Isso é para números que começam com um dígito

| separa subexpressões

[.]\d+(e[+-]?\d+)? isso é para números começando com '.'

) fim das expressões

  • Para números começando com '.'

[.] o primeiro caractere é um ponto (entre colchetes ou então é um caractere curinga)

\d+ um ou mais dígitos

(e[+-]?\d+)? esta é uma notação científica opcional (0 ou 1 correspondências devido à terminação '?')

  • Para números que começam com um dígito

\d+ um ou mais dígitos

([.]\d*)? opcionalmente, podemos ter um caractere de ponto e zero ou mais dígitos após ele

(e[+-]?\d+)? esta é uma notação científica opcional

  • Notação científica

e literal que especifica o expoente

[+-]? sinal de expoente opcional

\d+ um ou mais dígitos

Todos aqueles combinados:

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

Para aceitar Etambém:

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

( Casos de teste )

Yannis T
fonte
4

É simples: você usou Java e deve usar em \\.vez de \.(pesquise por escape de caractere em Java).

o sem nome
fonte
Você provavelmente está correto ... a mensagem de erro parece um erro de sintaxe de linguagem de programação em vez de um erro do analisador regex.
JDB ainda se lembra de Monica
3

Este funcionou para mim:

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

Você também pode usar este (sem parâmetro nomeado):

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

Use algum testador de regex online para testá-lo (por exemplo, regex101)

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

Isso vai corresponder a:

  1. 1,2
  2. 12,3
  3. 1,2
  4. 12,3
Mihai Ciobanu
fonte
Embora este trecho de código seja bem-vindo e possa fornecer alguma ajuda, seria muito melhor se incluísse uma explicação de como e por que isso resolve o problema. Lembre-se de que você está respondendo às perguntas dos leitores no futuro, não apenas da pessoa que está perguntando agora! Por favor edite sua resposta para adicionar explicação, e dar uma indicação do que limitações e premissas se aplicam.
Toby Speight
Oh,
obrigado
0
[+-]?(([1-9][0-9]*)|(0))([.,][0-9]+)?

[+-]? - sinal de liderança opcional

(([1-9][0-9]*)|(0)) - inteiro sem zero à esquerda, incluindo zero único

([.,][0-9]+)? - parte fracionária opcional

Aleksei Gutikov
fonte
1
Dê mais informações - para pessoas que não conhecem as regexps, são hyerogliphs. Para as pessoas que os conhecem, eles não precisam disso.
peterh - Reintegrar Monica
0

Em C ++ usando a biblioteca regex

A resposta seria assim:

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

Observe que eu não pego o símbolo do sinal, se você quisesse com o símbolo do sinal, faria o seguinte:

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

Isso também separa um número normal ou um número decimal.

LuisDev99
fonte
0

Na notação c, o número flutuante pode ocorrer nas seguintes formas:

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

Para criar a expressão regular float, primeiro criarei "variável de expressão regular int":

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

Agora, vou escrever pequenos pedaços de expressão regular de float - a solução é concatá-los com o símbolo "|".

Pedaços:

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

Solução final (concanando pequenos pedaços):

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

Experimente esta solução.

Lola Gorochana
fonte
-1

para javascript

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

O que funcionaria para 1,23 1234,22 0 0,12 12

Você pode alterar as partes no {}para obter resultados diferentes no comprimento decimal e na frente do decimal também. Isso é usado em entradas para inserir o número e verificar cada entrada conforme você digita, permitindo apenas o que passa.

mjwrazor
fonte