YAML загружает 5e-6 как строку, а не число

Когда я загружаю число с помощью e, формирую дамп JSON с помощью YAML, число загружается как строка, а не как число с плавающей запятой.

Думаю, этот простой пример может объяснить мою проблему.

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 как строку, а не как число? Как я могу это исправить?


person Oren    schedule 26.05.2015    source источник
comment
возможный дубликат Отключить научную нотацию в выводе python json.dumps   -  person SiHa    schedule 26.05.2015
comment
@SiHa Это может быть способ избежать проблемы, но настоящая проблема в том, что YAML должен быть надмножеством JSON, а '1e-06`, когда вы выходите из json.dumps() , является правильным Номер JSON и AFAICT также правильный номер YAML. PyYAML просто не разбирает его правильно.   -  person Anthon    schedule 26.05.2015
comment
Хорошо, это была просто мысль ...   -  person SiHa    schedule 26.05.2015
comment
@Oren, я дополнительно обновил свой ответ, так как исходный шаблон, который я предложил, может иметь проблемы с сопоставлением чисел без точки или экспоненциальной части. ruamel.yaml правильно анализирует эти номера JSON без каких-либо дополнительных исправлений.   -  person Anthon    schedule 27.05.2015
comment
@Oren, просто отредактируйте свой yaml-файл с 1e-3 на 1.0e-3   -  person Koo    schedule 09.04.2021
comment
Привет, @Koo, json был создан автоматически из конвейера ..   -  person Oren    schedule 10.04.2021


Ответы (3)


Проблема заключается в том, что YAML Resolver настроен для сопоставления чисел с плавающей запятой следующим образом:

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() в неявном преобразователе.

Сопоставление чисел с плавающей запятой может быть исправлено, поэтому оно будет принимать значения с плавающей запятой с _4 _ / _ 5_, но без десятичной точки и с показателями без знака (т.е. подразумевается +):

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":

введите описание изображения здесь

Реализация 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 также видит как строку.

person Anthon    schedule 26.05.2015
comment
Если я правильно понял, это объективно баг в PyYAML? Вы отправили запрос на перенос, исправляющий это? - person Mark Amery; 16.06.2015
comment
@MarkAmery В прошлом году я отправил PR для PyYAML, который реинтегрировал две ветки кода (Python2 и Python3) без какой-либо реакции. Этот проект в настоящее время в лучшем случае находится в гибернации, и я не собираюсь тратить свое время на PR для PyYAML, пока он не пробудится . Позже я раздвоился и продолжил исправления (также некоторые из них были замечены в PyYAML), потому что мне нужно было двигаться вперед и я больше не мог ждать. Я думаю, что это ошибка, поскольку он не реализует принцип, согласно которому YAML является надмножеством JSON, или точное регулярное выражение, указанное в спецификации YAML. С этим изменением все существующие юниттесты PyYAML прошли, когда я попытался. - person Anthon; 16.06.2015
comment
@MarkAmery Чтобы быть ясным, я скорее исправил ошибки, которые я исправил в PyYAML, а затем создал форк источника для дополнительной функциональности (неприемлемой для PyYAML) и сохранил все в синхронизации. - person Anthon; 16.06.2015
comment
@Anthon Большое спасибо за ваши усилия. Я делаю научные расчеты с конфигурацией в YAML. Возможность писать числа в научном уведомлении очень помогает. - person dotcs; 23.10.2015
comment
@MarkAmery и Anthon, похоже, этот PR решает проблему: github.com/yaml/pyyaml/pull/ 174 - person cyberjoac; 18.07.2018
comment
@cyberjoac На самом деле это не так. Эти правила следует применять только при синтаксическом анализе YAML 1.2, а не при синтаксическом анализе YAML 1.1. В этот коммит также не добавлены тесты, проверяющие правильное поведение. - person Anthon; 18.07.2018
comment
Как вы нарисовали изображение? - person stackunderflow; 21.04.2021

я так думаю

1.0e-1

or

1.0E-1

Решили мою проблему. И мой код для чтения файла yaml выглядит следующим образом

import yaml


def read_config(path: str):
    """read yaml file"""
    with open(path, 'r') as f:
        data = yaml.safe_load(f)
    return data
person Jason Lin    schedule 24.06.2021

Я новичок в использовании YAML, поэтому не знаю, что лучше, но пишу либо

1.0e-1

or

1.0E-1

в моем файле YAML работает "из коробки". То есть иметь десятичную дробь с коэффициентом (без десятичной дроби у меня тоже были строки).

person Francisco C    schedule 06.06.2021