明智地解析科学记数法?

时间:2009-03-12 13:13:04

标签: regex parsing scientific-notation

我希望能够编写一个函数,它以科学记数法形式接收一个数字作为字符串,并将系数和指数分开作为单独的项目。我可以使用正则表达式,但传入的数字可能没有标准化,我更愿意能够规范化然后打破部分。

一位同事已经使用VB6获得了解决方案的一部分,但它并不完全存在,如下面的成绩单所示。

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

应该是1和6

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

正确

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

正确

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

应为-1.233456和-2

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

正确

有什么想法吗?顺便说一下,Clive是一个基于VBScript的CLI,可以在weblog上找到。

3 个答案:

答案 0 :(得分:71)

Google "scientific notation regexp"上显示了多项匹配,包括使用

this one不使用!!!!
*** warning: questionable ***
/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/

包括-.5e7和+ 00000e33等情况(您可能不希望这两种情况)。

相反,我会高度建议您使用Doug Crockford JSON website上的语法,该语法明确记录了JSON中数字的构成。以下是从该页面获取的相应语法图:

alt text
(来源:json.org

如果你查看他的json2.js脚本的第456行(在javascript中安全转换为JSON),你会看到正则表达式的这一部分:

/-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/
具有讽刺意味的是,它与他的语法图不符......(看起来我应该提交一个bug)我相信实现该语法图的正则表达式是这样的:

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

如果你想允许初始+,你得到:

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

根据自己的喜好添加捕获括号。

我还强烈建议您充实一堆测试用例,以确保包含您想要包含(或不包含)的可能性,例如:

allowed:
+3
3.2e23
-4.70e+9
-.2E-4
-7.6603

not allowed:
+0003   (leading zeros)
37.e88  (dot before the e)
祝你好运!

答案 1 :(得分:2)

根据评分最高的答案,我将正则表达式略微修改为/^[+\-]?(?=.)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/

这提供的好处是:

  1. 允许匹配.9之类的数字(我将(?:0|[1-9]\d*)设置为?的可选内容)
  2. 防止在开始时仅匹配运算符,并防止匹配零长度字符串(使用lookahead,(?=.)
  3. 阻止匹配e9,因为它需要科学计数法之前的\d

我的目标是使用它来捕获重要数字并进行重要数学运算。因此,我还将像捕获/^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/这样的组来对其进行分解。

关于如何从中获取重要数字的说明:

  1. 整个捕获就是您可以交给parseFloat()的号码
  2. 第1-3个匹配项将显示为未定义或字符串,因此将它们组合在一起(用undefined替换''可以得到可以提取有效数字的原始数字。

此正则表达式还防止匹配左填充的零,JavaScript有时会接受这种零,但是我已经看到了导致问题的原因,并且未对有效数字添加任何内容,因此我认为防止左填充的零是有好处的(尤其是在表单中)。但是,我确定可以修改正则表达式以吞噬左填充的零。

我看到此正则表达式的另一个问题是它与90.e9或其他此类数字不匹配。但是,我发现这种或类似的匹配极不可能,因为科学记数法中的惯例是避免使用此类数字。尽管您可以使用JavaScript输入它,但也可以轻松输入9.0e10并获得相同的有效数字。

更新

在测试中,我还捕获到可能与'.'相匹配的错误。因此,应将前瞻性修改为(?=\.\d|\d),这将导致最终的正则表达式:

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

答案 2 :(得分:1)

以下是我刚刚快速入侵的一些Perl代码。

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";