Parsing notação científica sensata?

Eu quero ser capaz de escrever uma function que recebe um número em notação científica como uma string e separa o coeficiente e o expoente como itens separados. Eu poderia usar apenas uma expressão regular, mas o número de input pode não ser normalizado e eu preferiria ser capaz de normalizar e depois separar as partes.

Um colega tem um meio de solução usando o VB6, mas não está lá, como mostra a transcrição abaixo.

cliVe> a = 1e6 cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a) coeff: 10 exponent: 5 

deveria ter sido 1 e 6

 cliVe> a = 1.1e6 cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a) coeff: 1.1 exponent: 6 

corrigir

 cliVe> a = 123345.6e-7 cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a) coeff: 1.233456 exponent: -2 

corrigir

 cliVe> a = -123345.6e-7 cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a) coeff: 1.233456 exponent: -2 

deve ser -1.233456 e -2

 cliVe> a = -123345.6e+7 cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a) coeff: 1.233456 exponent: 12 

corrigir

Alguma ideia? By the way, Clive é um CLI baseado em VBScript e pode ser encontrado no meu blog .

Google em “notação científica regexp” mostra uma série de correspondências, incluindo esta ( não use !!!! ) que usa

 *** warning: questionable *** /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/ 

que inclui casos como -.5e7 e + 00000e33 (ambos os quais você pode não querer permitir).

Em vez disso, recomendo que você use a syntax no site JSON de Doug Crockford, que documenta explicitamente o que constitui um número no JSON. Aqui está o diagrama de syntax correspondente obtido a partir dessa página:

texto alternativo http://sofpt.miximages.com/regex/number.gif

Se você olhar para a linha 456 do seu script json2.js (conversão segura de / para JSON em javascript), você verá esta parte de um regexp:

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

que, ironicamente, não corresponde ao seu diagrama de syntax … (parece que eu deveria apresentar um bug) Acredito que um regexp que implemente esse diagrama de syntax é este:

 /-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/ 

e se você quiser permitir um + inicial, você terá:

 /[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/ 

Adicione parênteses de captura ao seu gosto.

Eu também recomendo que você desenvolva vários casos de teste, para garantir que você inclua as possibilidades que deseja include (ou não include), como:

 allowed: +3 3.2e23 -4.70e+9 -.2E-4 -7.6603 not allowed: +0003 (leading zeros) 37.e88 (dot before the e) 

Boa sorte!

Aqui está um código Perl que acabei de hackear rapidamente.

 my($sign,$coeffl,$coeffr,$exp) = $str =~ /^\s*([-+])?(\d+)(\.\d*)?e([-+]?\d+)\s*$/; my $shift = length $coeffl; $shift = 0 if $shift == 1; my $coeff = substr( $coeffl, 0, 1 ); if( $shift || $coeffr ){ $coeff .= '.'. substr( $coeffl, 1 ); } $coeff .= substr( $coeffr, 1 ) if $coeffr; $coeff = $sign . $coeff if $sign; $exp += $shift; say "coeff: $coeff exponent: $exp"; 

Construindo a resposta mais bem classificada, eu modifiquei o regex ligeiramente para ser /^[+\-]?(?=.)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/ .

Os benefícios que isso oferece são:

  1. permite números correspondentes como .9 (eu fiz o (?:0|[1-9]\d*) opcional com ? )
  2. impede a correspondência apenas do operador no início e evita sequências de comprimento zero correspondentes (usa lookahead, (?=.) )
  3. impede a correspondência do e9 porque requer o \d antes da notação científica

Meu objective nisso é usá-lo para capturar valores significativos e fazer cálculos significativos. Então, eu também vou /^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/ com grupos assim: /^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/ .

Uma explicação de como obter números significativos disso:

  1. A captura inteira é o número que você pode entregar para parseFloat()
  2. As correspondências 1-3 aparecerão como indefinidas ou seqüências de caracteres, portanto, combiná-las (replace undefined por '' ) deve fornecer o número original do qual números significativos podem ser extraídos.

Essa regex também impede a correspondência de zeros à esquerda, que o JavaScript às vezes aceita, mas que eu vi causar problemas e que não adiciona nada a números significativos, então vejo impedindo zeros preenchidos à esquerda como um benefício (especialmente em formulários). No entanto, tenho certeza que o regex poderia ser modificado para devorar os zeros preenchidos à esquerda.

Outro problema que vejo com este regex é que ele não corresponderá a 90.e9 ou a outros números desse tipo. No entanto, acho que essa ou outras correspondências são altamente improváveis, já que é uma convenção na notação científica evitar tais números. Embora você possa inseri-lo em JavaScript, você pode facilmente entrar no 9.0e10 e obter os mesmos números significativos.

ATUALIZAR

No meu teste, eu também peguei o erro que poderia corresponder '.' . Portanto, o look-ahead deve ser modificado para (?=\.\d|\d) que leva ao regex final:

 /^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/