web-dev-qa-db-ja.com

YAMLは5e-6を数値ではなく文字列としてロードします

Eを使用して数値をYAMLを使用してJSONダンプから読み込むと、数値は浮動小数点数ではなく文字列として読み込まれます。

この簡単な例で私の問題を説明できると思います。

import json
import yaml

In [1]: import json

In [2]: import yaml

In [3]: All = {'one':1,'low':0.000001}

In [4]: jAll = json.dumps(All)

In [5]: yAll = yaml.safe_load(jAll)

In [6]: yAll
Out[6]: {'low': '1e-06', 'one': 1}

YAMLは1e-06を数値ではなく文字列としてロードしますか?どうすれば修正できますか?

25
Oren

問題は、YAMLリゾルバーがフロートに一致するように次のように設定されているという事実にあります。

_Resolver.add_implicit_resolver(
    u'tag:yaml.org,2002:float',
    re.compile(u'''^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?
    |\\.[0-9_]+(?:[eE][-+][0-9]+)?
    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
    |[-+]?\\.(?:inf|Inf|INF)
    |\\.(?:nan|NaN|NAN))$''', re.X),
    list(u'-+0123456789.'))
_

一方、 YAML仕様 は、科学的記数法の正規表現を次のように指定します。

_-? [1-9] ( \. [0-9]* [1-9] )? ( e [-+] [1-9] [0-9]* )?
_

後者はドットをオプションにしますが、これは暗黙のリゾルバーの上記のre.compile()パターンにはありません。

浮動小数点のマッチングを修正して、e/Eを含むが、小数ドットを含まず、指数を含まない浮動小数点値を受け入れるようにすることができます(つまり、_+_を意味します)。

_import yaml
import json
import re

All = {'one':1,'low':0.000001}

jAll = json.dumps(All)

loader = yaml.SafeLoader
loader.add_implicit_resolver(
    u'tag:yaml.org,2002:float',
    re.compile(u'''^(?:
     [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
    |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
    |\\.[0-9_]+(?:[eE][-+][0-9]+)?
    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
    |[-+]?\\.(?:inf|Inf|INF)
    |\\.(?:nan|NaN|NAN))$''', re.X),
    list(u'-+0123456789.'))

data = yaml.load(jAll, Loader=loader)
print 'data', data
_

結果:

_data {'low': 1e-06, 'one': 1}
_

JSONが数値で許可するものと、YAML 1.2仕様の正規表現との間に不一致があります(数値に必要なドットとeが小文字であることに関して)。 JSON仕様 は、「e/E」の前にドットを必要とせず、「e/E」の後に記号を必要としないという点で、IMOは非常に明確です。

enter image description here

PyYAML実装は、部分的にJSON仕様に従って、部分的に正規表現に対してフロートを照合し、有効であるはずの数値で失敗します。

ruamel.yaml (これはPyYAMLの拡張バージョンです)、これらの更新されたパターンがあり、正しく機能します:

_import ruamel.yaml
import json

All = {'one':1,'low':0.000001}

jAll = json.dumps(All)

data = ruamel.yaml.load(jAll)
print 'data', data
_

出力付き:

_data {'low': 1e-06, 'one': 1}
_

ruamel.yamlは、数値「1.0e6」も受け入れます。これは、PyYAMLも文字列と見なします。

28
Anthon