Python: 将包含字符串的复杂字典从Unicode转换为ASCII

10

可能重复:
如何在Python中从JSON获取字符串对象而不是Unicode对象?

我有很多作为多级字典的输入,这些字典是通过JSON API调用解析出来的。字符串都是unicode格式,这意味着有很多像u'这样的东西'。我正在使用jq来分析结果,并需要将这些结果转换为ASCII。

我知道我可以编写一个函数来进行直接转换:

def convert(input):
    if isinstance(input, dict):
        ret = {}
        for stuff in input:
            ret = convert(stuff)
    elif isinstance(input, list):
        ret = []
        for i in range(len(input))
            ret = convert(input[i])
    elif isinstance(input, str):
        ret = input.encode('ascii')
    elif :
        ret = input
    return ret

这个算法是否正确?不确定。但我想问的不是这个。

我的问题是,这是一个典型的暴力解决方案。一定有更好的方法。一种更 Pythonic 的方式。我不是算法专家,但这个算法似乎也不是特别快。

那么有没有更好的方法?如果没有,这个函数能否被改进...?


回答后编辑

Mark Amery 的回答是正确的,但我想发布一个修改版本。他的函数适用于 Python 2.7+,而我使用的是2.6,因此必须进行转换:

def convert(input):
    if isinstance(input, dict):
        return dict((convert(key), convert(value)) for key, value in input.iteritems())
    elif isinstance(input, list):
        return [convert(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

1
如果您正在使用Python 2,则unicode不是str的实例,而是unicode的实例。此外,在listdict处理中,您的做法有误。 - agf
对于列表情况,您可能希望考虑处理任何可迭代对象。无论如何,您都可以用ret = [convert(x) for x in input]替换if语句的分支。此外,请检查您的字典情况。ret将仅包含字典中最后一个键转换的内容。 - Michael Mior
@MichaelMior 处理任何可迭代对象的问题在于,并非所有可迭代对象都类似于列表。例如,字典是可迭代的,但是如果 input 是一个字典,那么 ret = [convert(x) for x in input] 显然不是我们想要的。 - Mark Amery
@MarkAmery 当然。字典需要单独处理。 - Michael Mior
1个回答

30

递归似乎是这里的解决方法,但如果你使用的是 Python 2.xx 版本,你需要检查 unicode 而不是 strstr 类型表示字节串,而 unicode 类型表示 unicode 字符串;它们都没有继承关系,在解释器中显示的带有 u 的是 unicode 类型字符串)。

此外,您发布的代码中还有一个小语法错误(结尾的 elif: 应该是一个 else),并且在输入为字典或列表的情况下,您没有返回相同的结果结构。(对于字典的情况,您返回的是最终键的转换版本;对于列表的情况,您返回的是最终元素的转换版本。两者都不正确!)

您还可以通过使用理解(comprehensions)使您的代码更加美观和 Pythonic。

因此,这是我建议的做法:

def convert(input):
    if isinstance(input, dict):
        return {convert(key): convert(value) for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [convert(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

最后一件事情。我把encode('ascii')改为了encode('utf-8')。我的理由如下:任何只包含ASCII字符集中的字符的Unicode字符串,在使用ASCII编码和使用utf-8编码时将被表示为相同的字节串,因此如果你处理的Unicode字符串只使用ASCII字符,则使用utf-8而不是ASCII不会破坏任何内容,且在这种情况下修改是不可见的。然而,这个改变扩展了函数的作用范围,使其能够处理整个Unicode字符集中的字符,而不仅仅是ASCII字符,如果有必要的话。


1
除了你对递归的评论之外 :) 递归对于几乎任何类型的树遍历和大多数解析问题都非常有用。递归通常是“前进的方式”,特别是在函数式编程方面。 - Joel Cornett
1
@JoelCornett 好的,我的评论并不是要广泛地反对递归;我能看出在树遍历问题中递归是有意义的,我猜测许多解析问题是其中的一个子集。我只是很新手,而且不是计算机科学背景,所以我还没有遇到过那种问题。我看到的递归示例往往是无意义和牵强附会的,并将其应用于迭代更清晰的情况下。这是我第一次突然意识到“哇,递归真的简化了这个问题”,这对我来说很激动 :)。 - Mark Amery
谢谢,这真的很好。比这个问题所谓的重复的任何答案都要好得多。 - Dreen
你的代码出了些问题,所以我做了以下修改: def unicode_to_string(text): if type(text) is unicode: return text.encode('ascii', 'ignore') if type(text) is list: return [unicode_to_string(a) for a in text] if type(text) is dict: return dict((unicode_to_string(key), unicode_to_string( value)) for key, value in text.iteritems()) return text - Gil Zellner
2
非常好用,谢谢。 - nishantvas
显示剩余2条评论

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