Estou tentando localizar e substituir todos os números em um corpo de texto. Encontrei alguns exemplos de regex, que quase resolvem o problema, mas nenhum é perfeito ainda. O problema que tenho é que os números no meu texto podem ou não ter decimais e vírgulas. Por exemplo:
"A raposa de 5.000 libras pulou uma cerca de 99.999.99998713 pés."
O regex deve retornar " 5000
" e " 99,999.99998713
". Exemplos que encontrei separando os números na vírgula ou estão limitados a duas casas decimais. Estou começando a entender o regex o suficiente para ver por que alguns exemplos são limitados a duas casas decimais, mas ainda não aprendi como superá-lo e também incluir a vírgula para obter a sequência inteira.
Aqui está minha última versão:
[0-9]+(\.[0-9][0-9]?)?
Que retorna, " 5000
", " 99,99
", " 9.99
" e " 998713
" para o texto acima.
.,.,.
ou9,9,9,9
ou9,9.99.9
. Essas regexes não exigem que os números estejam no formato adequado e, na pior das hipóteses, tratam a pontuação como números. Existem alguns ajustes opcionais possíveis (por exemplo, se permitir zeros à esquerda e à direita), mas algumas das respostas que estou vendo estão totalmente incorretas. Eu realmente não gosto de downvoting, especialmente em tentativas honestas, mas sinto que as respostas aqui precisam ser limpas. Esta é uma pergunta comum e com certeza será feita novamente.Respostas:
EDIT: Como isso gerou muitas visualizações, deixe-me começar dando a todos o que eles procuraram no Google:
Agora que isso já foi resolvido, a maior parte do que se segue é um comentário sobre como a regex complexa pode ficar se você tentar ser inteligente com ela e por que deve buscar alternativas. Leia por sua própria conta e risco.
Esta é uma tarefa muito comum, mas todas as respostas que eu vejo aqui até agora aceitará entradas que não correspondem a sua formatação de números, tais como
,111
,9,9,9
ou mesmo.,,.
. Isso é bastante simples de corrigir, mesmo se os números estiverem incorporados em outro texto. IMHO qualquer coisa que não consegue puxar 1,234.56 e 1234- e somente aqueles números out ofabc22 1,234.56 9.9.9.9 def 1234
é uma resposta errada.Em primeiro lugar, se você não precisa fazer tudo em uma regex, não faça. É difícil manter uma única regex para dois formatos de número diferentes, mesmo quando eles não estão incorporados em outro texto. O que você realmente deve fazer é dividir tudo em espaços em branco e, em seguida, executar duas ou três expressões regulares menores nos resultados. Se essa não for uma opção para você, continue lendo.
Padrão básico
Considerando os exemplos que você deu, aqui está um regex simples que permite praticamente qualquer número inteiro ou decimal no
0000
formato e bloqueia todo o resto:Aqui está um que requer
0,000
formato:Coloque-os juntos e as vírgulas se tornam opcionais, desde que sejam consistentes:
Números embutidos
Os padrões acima requerem que toda a entrada seja um número. Você está procurando por números embutidos no texto, então você tem que afrouxar essa parte. Por outro lado, você não quer que ele veja
catch22
e pense que encontrou o número 22. Se você estiver usando algo com suporte para lookbehind (como .NET), isso é muito fácil: substitua^
por(?<!\S)
e$
por(?!\S)
e você estará bem ir:Se você está trabalhando com JavaScript ou Ruby ou algo assim, as coisas começam a parecer mais complexas:
Você terá que usar grupos de captura; Não consigo pensar em uma alternativa sem o apoio de olhar para trás. Os números que você deseja estarão no Grupo 1 (assumindo que a partida inteira seja o Grupo 0).
Validação e regras mais complexas
Acho que isso cobre sua pergunta, então se isso é tudo de que você precisa, pare de ler agora. Se você quiser ficar mais sofisticado, as coisas se tornam muito complexas muito rapidamente. Dependendo da sua situação, você pode bloquear um ou todos os seguintes:
Só por diversão, vamos supor que você queira bloquear os primeiros 3, mas permitir o último. O que você deveria fazer? Vou te dizer o que você deve fazer, você deve usar uma regex diferente para cada regra e restringir progressivamente suas correspondências. Mas pelo bem do desafio, veja como você faz tudo em um padrão gigante:
E aqui está o que significa:
Testado aqui: http://rextester.com/YPG96786
Isso permitirá coisas como:
Isso irá bloquear coisas como:
Existem várias maneiras de tornar este regex mais simples e curto, mas entenda que alterar o padrão afrouxará o que ele considera um número.
Uma vez que muitos mecanismos de regex (por exemplo, JavaScript e Ruby) não suportam o lookbehind negativo, a única maneira de fazer isso corretamente é com grupos de captura:
Os números que você está procurando estarão no grupo de captura 1.
Testado aqui: http://rubular.com/r/3HCSkndzhT
Uma nota final
Obviamente, este é um regex enorme, complicado e quase ilegível. Gostei do desafio, mas você deve considerar se realmente deseja usar isso em um ambiente de produção. Em vez de tentar fazer tudo em uma etapa, você pode fazer em duas: uma regex para capturar qualquer coisa que possa ser um número, depois outra para eliminar o que não for um número. Ou você pode fazer algum processamento básico e, em seguida, usar as funções integradas de análise de números da sua linguagem. Sua escolha.
fonte
Alguns dias atrás, trabalhei no problema de remover os zeros à direita da sequência de um número .
Na continuidade desse problema, acho este interessante porque amplia o problema para números compreendendo vírgulas.
Peguei o padrão de regex que escrevi no problema anterior em que trabalhei e o melhorei para que pudesse tratar os números com vírgulas como uma resposta para esse problema.
Tenho me deixado levar pelo meu entusiasmo e pelo meu gosto por regexes. Não sei se o resultado se encaixa exatamente à necessidade expressa por Michael Prescott. Eu estaria interessado em saber os pontos que estão em excesso ou em falta na minha regex e em corrigi-la para torná-la mais adequada para você.
Agora, depois de uma longa sessão de trabalho nesta regex, tenho uma espécie de peso no cérebro, então não estou fresco o suficiente para dar muitas explicações. Se os pontos são obscuros, e se alguém pode vir a se interessar o suficiente, por favor, pergunte-me.
A regex é construída de forma que possa detectar os números expressos em notação científica 2E10 ou mesmo 5.22.454.12E-00.0478 , removendo zeros desnecessários nas duas partes de tais números também. Se um expoente for igual a zero, o número é modificado para que não haja mais expoente.
Eu coloquei alguma verificação no padrão para que alguns casos particulares não correspondam, por exemplo '12 ..57 ' não correspondessem. Mas em ', 111' a string '111' corresponde porque a vírgula anterior é considerada uma vírgula não estando em um número, mas uma vírgula de frase.
Acho que o gerenciamento de vírgulas deve ser melhorado, porque me parece que existem apenas 2 dígitos entre vírgulas na numeração indiana. Não será difícil corrigir, presumo
A seguir está um código que demonstra como meu regex funciona. Existem duas funções, conforme se quer os números '.1245' sejam transformados em '0,1245' ou não. Eu não ficaria surpreso se erros ou correspondências indesejadas ou não correspondências permanecessem para certos casos de cadeias de números; então gostaria de conhecer esses casos para entender e corrigir a deficiência.
Peço desculpas por este código escrito em Python, mas regexes são trans-idioma e acho que todos serão capazes de entender o padrão do reex
resultado
fonte
O regex abaixo corresponderá a ambos os números do seu exemplo.
Ele retornará 5000 e 99.999.99998713 - correspondendo aos seus requisitos.
fonte
this,that
.\b\d[\d,.]+\b
9....9
ou1,,,,X
(embora o X não seja incluído na partida).\b\d[\d,.]*\b
está perto o suficiente para que, se você editar sua resposta, eu removesse o -1. Deve ser um * em vez de um +;\b\d[\d,.]+\b
não permite números de um único dígito.Tomando uma certa liberdade com os requisitos, você está procurando
Mas observe que isso corresponderá, por exemplo, 11,11,1
fonte
\d+([\d,]?\d)*(\.\d+)?
vez de\d+(,\d+)*(\.\d+)?
? Acho que dariam correspondências equivalentes, embora os grupos de captura fossem diferentes.Isso pressupõe que sempre haja pelo menos um dígito antes ou depois de qualquer vírgula ou decimal e também pressupõe que haja no máximo um decimal e que todas as vírgulas precedam o decimal.
fonte
999999,9,9,9,9
.(,\d+)
para(,\d\d\d)
eu acho.Este regex:
Correspondeu a todos os números da string:
1 1,0 0,1 1,001 1.000 1.000.000 1.000,1 1.000,1 1.323.444.000 1.999 1.222.455.666,0 1.244
fonte
Aqui está uma regex:
que aceita números:
123456789
,123.123
123 456 789
,123 456 789.100
,123,456
,3,232,300,000.00
Testes: http://regexr.com/3h1a2
fonte
Aqui está outra construção que começa com o formato de número mais simples e, em seguida, de uma forma não sobreposta, adiciona progressivamente formatos de número mais complexos:
Java regep:
Como uma string Java (observe o \ extra necessário para escapar para \ e. Uma vez que \ e. Têm um significado especial em uma expressão regular quando por conta própria):
Explicação:
Esta regexp tem a forma A | B | C | D | E | F onde A, B, C, D, E, F são as próprias regexps que não se sobrepõem. Geralmente, acho mais fácil começar com as correspondências mais simples possíveis, A. Se A perder as correspondências que você deseja, crie um B que seja uma pequena modificação de A e inclua um pouco mais do que você deseja. Então, com base em B, crie um C que capture mais, etc. Também acho mais fácil criar regexps que não se sobreponham; é mais fácil entender uma expressão regular com 20 expressões regulares não sobrepostas simples conectadas com ORs em vez de algumas expressões regulares com correspondência mais complexa. Mas, cada um com o seu!
A é (\ d) e corresponde exatamente a 0,1,2,3,4,5,6,7,8,9 que não pode ser mais simples!
B é ([1-9] \ d +) e corresponde apenas a números com 2 ou mais dígitos, o primeiro excluindo 0. B corresponde exatamente a um de 10,11,12, ... B não se sobrepõe a A, mas é uma pequena modificação de A.
C é (. \ D +) e corresponde apenas a um decimal seguido por um ou mais dígitos. C corresponde exatamente a um de .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .00 .01 .02 .... .23000 ... C permite eros à direita, o que eu prefiro: se forem dados de medição, o número de zeros à direita indica o nível de precisão. Se você não quiser os zeros à direita, altere (. \ D +) para (. \ D * [1-9]), mas isso também exclui .0 que eu acho que deveria ser permitido. C também é uma pequena modificação de A.
D é (\ d. \ D *) que é A mais decimais com zeros à direita. D corresponde apenas a um único dígito, seguido por um decimal, seguido por zero ou mais dígitos. D corresponde a 0. 0,0 0,1 0,2 ... 0,01000 ... 9. 9.0 9.1..0.0230000 .... 9.9999999999 ... Se você deseja excluir "0". em seguida, altere D para (\ d. \ d +). Se você quiser excluir zeros à direita, altere D para (\ d. \ D * [1-9]), mas isso exclui 2.0, que acho que deveria ser incluído. D não se sobrepõe a A, B ou C.
E é ([1-9] \ d +. \ D *) que é B mais decimais com zeros à direita. Se você deseja excluir "13", por exemplo, altere E para ([1-9] \ d +. \ D +). E não se sobrepõe a A, B, C ou D. E corresponde a 10. 10.0 10.0100 .... 99.9999999999 ... Zeros à direita podem ser tratados como em 4. e 5.
F é ([1-9] \ d {0,2} (, \ d {3}) + (. \ D *)?) E só combina números com vírgulas e possivelmente decimais, permitindo zeros à direita. O primeiro grupo ([1-9] \ d {0,2}) corresponde a um dígito diferente de zero seguido de zero, um ou mais dois dígitos. O segundo grupo (, \ d {3}) + corresponde a um grupo de 4 caracteres (uma vírgula seguida por exatamente três dígitos) e este grupo pode corresponder uma ou mais vezes (nenhuma correspondência significa que não há vírgulas!). Finalmente, (. \ D *)? não corresponde a nada ou corresponde. por si só ou corresponde a um decimal. seguido por qualquer número de dígitos, possivelmente nenhum. Novamente, para excluir itens como "1.111.", Altere (. \ D *) para (. \ D +). Zeros à direita podem ser tratados como em 4. ou 5. F não se sobrepõe a A, B, C, D ou E. Não consegui pensar em uma expressão regular mais fácil para F.
Deixe-me saber se você está interessado e posso editar acima para lidar com os zeros à direita conforme desejado.
Aqui está o que corresponde a regexp e o que não:
fonte
\ b -------> limite de palavra
\ d + ------> um ou dígito
, --------> contendo vírgulas,
Por exemplo:
sddsgg 70.000 sdsfdsf fdgfdg70,00
sfsfsd 5,44,4343 5,7788,44 555
Vai corresponder a:
70,
5,
44,
, 44
fonte
Isso corresponderia a qualquer número pequeno ou grande como a seguir com ou sem vírgula
ou
fonte