web-dev-qa-db-ja.com

科学表記法を賢く構文解析していますか?

科学表記で数値を文字列として受け取り、係数と指数を別々の項目として分割する関数を記述できるようにしたいと考えています。正規表現を使用することもできますが、着信番号が正規化されていない可能性があるため、正規化してパーツを分割できるようにしたいと考えています。

同僚は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 にあります。

42
bugmagnet

Google on "scientific notation regexp" は、 this onedo n't use it !!!!)を使用します

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

これには、-。5e7や+ 00000e33などのケースが含まれます(どちらも許可したくない場合があります)。

代わりに、highlyDoug Crockfordの JSON Webサイト の構文を使用して、JSONで数値を構成するものを明示的に文書化することをお勧めします。これは、そのページから取得した対応する構文図です。

alt text
(ソース: json.org

彼の json2.js スクリプト(JavaScriptでのJSONとの安全な変換)の456行目を見ると、正規表現のこの部分が表示されます。

/-?\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)

幸運を!

75
Jason S

最高評価の回答を基に、正規表現を/^[+\-]?(?=.)(?: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が受け入れることもあるが問題を引き起こし、重要な数値に何も追加しない左パディングゼロの照合も防止します。そのため、左パディングゼロの防止は利点として(特にフォームでは)わかります。ただし、正規表現を変更して、左側に埋め込まれたゼロを混乱させることができると私は確信しています。

この正規表現で見られるもう1つの問題は、_90.e9_または他のそのような数値と一致しないことです。ただし、このような数字を避けるのは科学表記法の慣例であるため、これまたは類似の一致はほとんどありません。 JavaScriptで入力することもできますが、_9.0e10_と同じくらい簡単に入力して、同じ有意な数値を得ることができます。

[〜#〜]更新[〜#〜]

テストでは、_'.'_と一致する可能性があるというエラーも検出しました。したがって、先読みを_(?=\.\d|\d)_に変更して、最終的な正規表現を作成する必要があります。

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

これが私がすぐにハッキングした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";
1
Brad Gilbert