如何强制PyYAML将字符串加载为Unicode对象?

33

PyYAML包会将未标记的字符串根据其内容加载为unicode或str对象。

我想在整个程序中都使用unicode对象(但不幸的是,现在还无法切换到Python 3)。

是否有一种简单的方法可以强制PyYAML始终将字符串加载为unicode对象?我不想用!!python/unicode标记来破坏我的YAML文件。

# Encoding: UTF-8

import yaml

menu= u"""---
- spam
- eggs
- bacon
- crème brûlée
- spam
"""

print yaml.load(menu)

输出结果为:['spam', 'eggs', 'bacon', u'crème brûlée', 'spam']

我想要的输出结果为:[u'spam', u'eggs', u'bacon', u'crème brûlée', u'spam']

2个回答

29

这是一个版本,通过始终输出unicode,覆盖了 PyYAML 对字符串的处理。实际上,这可能是我发布的其他响应的相同结果,只不过更短(例如,您仍然需要确保自定义类中的字符串被转换为unicode,或者如果您使用自定义处理程序,则自己传递unicode字符串):

# -*- coding: utf-8 -*-
import yaml
from yaml import Loader, SafeLoader

def construct_yaml_str(self, node):
    # Override the default string handling function 
    # to always return unicode objects
    return self.construct_scalar(node)
Loader.add_constructor(u'tag:yaml.org,2002:str', construct_yaml_str)
SafeLoader.add_constructor(u'tag:yaml.org,2002:str', construct_yaml_str)

print yaml.load(u"""---
- spam
- eggs
- bacon
- crème brûlée
- spam
""")

(上述代码输出结果为[u'spam', u'eggs', u'bacon', u'crème brûlée', u'spam']

我没有在LibYAML(基于c的解析器)上测试过它,因为我无法编译它,所以我将保留另一个答案不变。


非常完美,谢谢!它可以在自定义类中使用字符串,并且可以与LibYAML的CLoader一起使用。看起来更加简洁 :) 再次感谢! - Petr Viktorin
4
这个答案被接受已经超过两年了,但是 pyYAML 仍然返回 str 对象。现在是否有更简单的方法来强制输出全部为 Unicode?我希望得到一个更新的回答。 - Hubro
我不这么认为,而且我认为这很不可能被添加。但是在Python 3中它可以正常工作。 - Petr Viktorin
1
这使得对于使用wxPython进行GUI应用程序开发的Pythonia的一半人来说,YAML变得非常烦人。虽然比XML等讽刺性数据格式要少烦人得多,但仍然相当烦人。 - zxq9

3
以下是您可以使用的函数,用于将str替换为来自PyYAML解码输出的unicode类型:
def make_str_unicode(obj):
    t = type(obj)

    if t in (list, tuple):
        if t == tuple:
            # Convert to a list if a tuple to 
            # allow assigning to when copying
            is_tuple = True
            obj = list(obj)
        else: 
            # Otherwise just do a quick slice copy
            obj = obj[:]
            is_tuple = False

        # Copy each item recursively
        for x in xrange(len(obj)):
            obj[x] = make_str_unicode(obj[x])

        if is_tuple: 
            # Convert back into a tuple again
            obj = tuple(obj)

    elif t == dict: 
        for k in obj:
            if type(k) == str:
                # Make dict keys unicode
                k = unicode(k)
            obj[k] = make_str_unicode(obj[k])

    elif t == str:
        # Convert strings to unicode objects
        obj = unicode(obj)
    return obj

print make_str_unicode({'blah': ['the', 'quick', u'brown', 124]})

不是我想看到的答案 :( 这个函数可能适用于大多数常见的YAML文件,但并非所有文件都适用。字典键可能不是字符串,并且YAML允许存储自定义类型,其中可能包含字符串。 - Petr Viktorin
如果键不是 str 类型,它们就不会被转换为 unicode 类型(如果您查看代码的话)。我同意这不是一个很好的解决方案,但它可以工作。尝试使用 make_str_unicode({0: [u'the', u'quick', u'brown', 124]}),它将保留整数。此外,如果您进一步查看代码,它只处理 listtupledictsstr(其他类型/类将保持原样)。 - cryo
如果您使用自定义类型,则处理程序可能需要将 str 对象自行转换为 unicode(或添加 elif isinstance(obj, mycustomtype: ... 并单独处理它们)。 - cryo
没问题,我觉得我可能会使用另一个解决方案,因为它更短/更快 :-) - cryo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接