【问题标题】:Parsing scientific notation sensibly?合理地解析科学记数法?
【发布时间】:2010-10-12 22:04:49
【问题描述】:

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

一位同事使用 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 找到。

【问题讨论】:

  • 拥有一个有效输入 => 输出的列表比你当前的、损坏的实现的输出更有帮助。
  • “应该是-1.233456和-2”应该是“应该是-1.233456和-7”,对吧?
  • 我不这么认为。 -1.233456e-7 也可以表示为 -0.01233456

标签: regex parsing scientific-notation


【解决方案1】:

"scientific notation regexp" 上的 Google 显示了许多匹配项,包括使用了的 this one不要使用它!!!!

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

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

相反,我强烈建议您使用 Doug Crockford 的 JSON website 中的语法,该语法明确记录了 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)

祝你好运!

【讨论】:

  • ...?只需使用 JSON 网站中显示的正则表达式/图表即可。
  • 那你为什么不试试前一个正则表达式,即语句“如果你也想允许一个初始 +”之前的那个?
  • 我知道这是一个非常古老的论坛,但想指出一些事情。看起来您的模式允许这种类型的条目“e324ewfg”,这显然不是一个有效的数字。
  • 发布的正则表达式不包括开头的^ 或结尾的$,这将阻止这些,如果匹配一个数字,则应使用;但一些正则表达式的使用模式更大。
  • @GerardONeill 呵呵——你是对的;我想知道为什么有人花了 12 年才发现我在将语法图转录为正则表达式时的错误:-)
【解决方案2】:

根据评分最高的答案,我将正则表达式稍微修改为 /^[+\-]?(?=.)(?: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 替换为 '')应该给出可以从中提取有效数字的原始数字。

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

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

更新

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

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

【讨论】:

    【解决方案3】:

    这是我刚刚快速编写的一些 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";
    

    【讨论】:

      猜你喜欢
      • 2012-02-22
      • 2023-02-21
      • 1970-01-01
      • 2017-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多