使用 PyYaml 加载特殊字符

Loading special characters with PyYaml

我正在努力在一个简单的 python 3.6 脚本中加载表情符号字符列表。 YAML结构基本如下:

-    
- 
- 

我的 python 脚本如下所示:

import yaml
f = open('emojis.yml')
EMOJIS = yaml.load(f)
f.close()

我遇到以下异常:

yaml.reader.ReaderError: unacceptable character #x001d: special characters are not allowed in "emojis.yml", position 2

我看到了 allow_unicode=True 选项,但似乎只适用于 yaml.dump。似乎人们在 Python2 中遇到过类似问题,但由于所有字符串都应该是 unicode,我无法弄清楚为什么这不起作用。

我也试过用引号括起我的表情符号,并为 'tag:yaml.org,2002:str' 使用客户构造函数。我的自定义构造函数甚至从未被命中,大概是因为 yaml 库无法将我的表情符号识别为字符串类型。当我将表情符号直接定义为源代码中的字符串时,我也观察到相同的行为。

有没有办法用 PyYAML 加载包含表情符号的 yaml 文件?

更新

最新版pyyaml修复了这个bug,升级到pyyaml>=5


原回答

这似乎是 pyyaml 中的一个错误,解决方法是使用它们的转义序列:

$ cat test.yaml
- "\U0001f642"
- "\U0001f601"
- "\U0001f62c"

$ python
...
>>> yaml.load(open('test.yaml'))
['', '', '']

你应该升级到 ruamel.yaml(免责声明:我是那个包的作者),它有这个,以及许多其他长期存在的 PyYAML 问题,已修复:

import sys
from ruamel.yaml import YAML

yaml = YAML()

with open('emojis.yml') as fp:
    idx = 0
    for c in fp.read():
        print('{:08x}'.format(ord(c)), end=' ')
        idx += 1
        if idx % 4 == 0:
            print()

with open('emojis.yml') as fp:
    data = yaml.load(fp)
yaml.dump(data, sys.stdout)

给出:

0000002d 00000020 0001f642 0000000a 
0000002d 00000020 0001f601 0000000a 
0000002d 00000020 0001f62c 0000000a 
['', '', '']

如果你真的必须坚持使用 PyYAML,你可以这样做:

import yaml.reader
import re

yaml.reader.Reader.NON_PRINTABLE = re.compile(
    u'[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]')

消除错误。


从版本 0.15.16 开始,ruamel.yaml 现在也转储所有补充平面 Unicode 而不会恢复到 \Uxxxxxxxx(可在新的 API 中通过 .unicode_supplementary, 并取决于 allow_unicode).