46

我希望能够编写一个函数,它接收科学计数法中的数字作为字符串,并将系数和指数拆分为单独的项目。我可以只使用正则表达式,但传入的数字可能没有被规范化,我希望能够规范化然后将部分分解。

一位同事使用 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,可以在我的博客上找到。

4

4 回答 4

82

谷歌上的“科学记数法正则表达式”显示了许多匹配项,包括这个不要使用它!!!!)它使用

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

其中包括诸如 -.5e7 和 +00000e33 之类的情况(您可能不想允许这两种情况)。

相反,我强烈建议您使用 Doug Crockford 的JSON 网站上的语法,该网站明确记录了 JSON 中数字的构成。这是从该页面获取的相应语法图:

替代文字
(来源:json.org

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

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

具有讽刺意味的是,这与他的语法图不匹配....(看起来我应该提交一个错误)我相信实现该语法图的正则表达式是这个:

/-?(?: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)

祝你好运!

于 2009-03-18T15:07:23.457 回答
4

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

这提供的好处是:

  1. 允许匹配数字,例如.9(我用 做了(?:0|[1-9]\d*)可选?
  2. 防止仅匹配开头的运算符并防止匹配零长度字符串(使用前瞻,(?=.)
  3. 防止匹配e9,因为它需要\d在科学记数法之前

我的目标是用它来捕捉重要的数字和做重要的数学。所以我也打算用这样的捕获组来分割它:/^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/.

关于如何从中获得有效数字的解释:

  1. 整个捕获是您可以交给的号码parseFloat()
  2. 匹配项 1-3 将显示为未定义或字符串,因此将它们组合起来(将undefined's替换为'')应该给出可以从中提取有效数字的原始数字。

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

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

更新

在我的测试中,我还发现了它可以匹配的错误'.'。因此,应该将前瞻修改为(?=\.\d|\d)最终的正则表达式:

/^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/
于 2018-08-10T16:25:43.423 回答
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";
于 2009-03-18T04:01:56.240 回答
1

在@Troy Weber 的基础上,我建议

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

为避免匹配3.,根据@Jason S 的规则

于 2022-02-17T18:38:23.143 回答