2015-05-26 10 views
8

Wenn ich eine Zahl mit einem JSON-Dump mit YAML lade, wird die Nummer als String und nicht als Float geladen.YAML lädt 5e-6 als String und keine Zahl

Ich denke, dieses einfache Beispiel kann mein Problem erklären.

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 Lasten 1e-06 als String und nicht als Zahl? Wie kann ich es reparieren?

+0

möglich Duplikat von [Disable wissenschaftliche Notation in Python json.dumps Ausgabe] (http://stackoverflow.com/questions/18936554/disable-scientific-notation-in-python-json-dumps-output) – SiHa

+3

@SiHa That könnte ein Weg sein, das Problem zu vermeiden, aber das eigentliche Problem ist, dass YAML eine Obermenge von JSON und '1e-06' sein soll, wenn Sie aus der 'json.dumps()' ** ist ** ein Korrekt JSON-Nummer und AFAICT auch eine korrekte YAML-Nummer. PyYAML analysiert es nicht richtig. – Anthon

+0

OK, war nur ein Gedanke ... – SiHa

Antwort

10

Das Problem liegt in der Tatsache, dass die YAML Resolver eingerichtet ist Schwimmern übereinstimmen, wie folgt:

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'-.')) 

während die YAML spec gibt den regulären Ausdruck für wissenschaftliche Notation als:

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

letzterem macht den Punkt optional, wobei es sich nicht um das obige re.compile() Muster handelt.

gesuchte von Schwimmern befestigt werden kann, so wird es Gleitkommawerte mit einem e/E akzeptiert aber ohne Dezimalpunktes und mit Exponenten ohne Vorzeichen (dh + impliziert):

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'-.')) 

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

ergibt:

Es gibt Diskrepanzen zwischen dem, was JSON in Zahlen zulässt, und der Regex in der Spezifikation YAML 1.2 (bezüglich des erforderlichen Punkts in der Zahl und e in Kleinbuchstaben). Die JSON specification ist IMO sehr klar, dass sie nicht den Punkt benötigt, bevor die E/E 'noch das erfordert ist ein Zeichen nach dem ‚e/E‘:

enter image description here

Die PyYAML Implementierung überein schwebt teilweise gemäß der JSON-Spezifikation und teilweise gegen die Regex und scheitert an Zahlen, die gültig sein sollten.

ruamel.yaml (das ist meine erweiterte Version PyYAML), hat diese aktualisierte Muster und funktioniert einwandfrei:

import ruamel.yaml 
import json 

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

jAll = json.dumps(All) 

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

mit Ausgang:

data {'low': 1e-06, 'one': 1} 

ruamel.yaml nimmt auch die Zahl ‚1,0E6 ', die PyYAML auch als String sieht.

+1

Wenn ich das richtig verstehe, ist das objektiv ein Fehler in PyYAML? Hast du eine Pull-Anfrage eingereicht, die es repariert? –

+1

@MarkAmery Ich habe letztes Jahr eine PR für PyYAML eingereicht, die die beiden Codezweige (Python2 und Python3) ohne jegliche Reaktion reintegrierte. Dieses Projekt ist derzeit im besten Fall hybernierend und ich werde meine Zeit auf PRs für PyYAML erst dann verschwenden erwacht wieder. Später gabelte ich weiter und fuhr mit Fixes fort (auch einige auf PyYAML), weil ich vorwärts gehen musste und nicht länger warten konnte. Ich denke, das ist ein Fehler, da es nicht das Prinzip implementiert, dass YAML eine Obermenge von JSON ist, noch die exakte Regex, die in der YAML-Spezifikation angegeben ist. Mit dieser Änderung wurden alle bestehenden PyYAML-Unittests bestanden, als ich es versuchte. – Anthon

+0

@MarkAmery Um klar zu sein, hatte ich viel lieber die Fehler behoben, die ich in PyYAML repariert habe und dann die Quelle für die zusätzliche Funktionalität (nicht akzeptabel für PyYAML) ausgabeln und die Dinge synchron halten. – Anthon