str和unicode之间的区别:为什么Redis在传递unicode时会返回二进制数据?

时间:2016-03-01 15:05:32

标签: python string unicode utf-8 redis

关于数据类型strunicode之间的区别two questions之后,我仍对以下内容感到困惑。

在第1区,我们看到城市的类型为unicode,正如我们所料。

然而在第2区中,在通过磁盘往返(redis)之后,城市的类型为str(且表示形式不同)。

在磁盘上存储utf-8的{​​{3}},读入unicode,并在utf-8中回写在某处失败。

为什么type(city) str而不是unicode的第二个实例?

同样重要的是,这有关系吗?你是否关心你的变量是unicode还是str,或者只要代码“做正确的事情”,你是否忘记了差异?

# -*- coding: utf-8 -*-

# Block 1
city = u'Düsseldorf'
print city, type(city), repr(city)
# Düsseldorf <type 'unicode'> u'D\xfcsseldorf'

# Block 2
import redis
r_server = redis.Redis('localhost')
r_server.set('city', city)
city = r_server.get('city')
print city, type(city), repr(city)
# Düsseldorf <type 'str'> 'D\xc3\xbcsseldorf'

2 个答案:

答案 0 :(得分:11)

教条?

使用字符集和编码并不是教条主义 - 这是必要的。 希望您已经阅读了足以理解为什么我们使用了这么多字符集。 Unicode显然是前进的方向(映射了所有字符),但是如何将Unicode字符从一台机器传输到另一台机器,或者将其保存到磁盘?

我们可以使用Unicode点值,但由于Unicode点实际上是32位,因此需要将每个字符保存/传输为整个32位(也称为UTF-32)。 a将被编码为0x00000061 - 这只是一个字符的浪费。在处理大多数ASCII时,UTF-16的浪费要少一些,但UTF-8是使用最少量位的最佳折衷方案。

在代码中使用解码的Unicode显然使开发人员不必考虑编码的复杂性,例如与字符相等的字节数。

解决方案

Redis客户端

正如@ J.F.Sebastian所建议的那样,redis-py驱动程序在decode_responsesRedis类中包含Connection选项。设置为True时,客户端将使用encoding选项解码响应。默认情况下为encoding = utf-8

E.g。

r_server = redis.Redis('localhost', decode_responses=True)
city = r_server.get('city')
# city = <type 'unicode'>

包装类

发现decode_responses后不再需要。

<击>

<击>

似乎Redis驱动程序相当简单 - 如果你发送一个Unicode,它会将它转换为默认编码(大多数情况下是UTF-8)。在响应时,Redis不知道编码,因此返回str以供您解密。

因此,如果在发送到Redis之前将字符串编码为UTF-8并在响应时解码为UTF-8会更安全。其他DB驱动程序更高级,因此接收并返回Unicodes。

但是,当然,您不应该使用.encode().decode()来编写代码。常见的方法是形成“Unicode三明治”,以便外部数据在输入时解码为Unicode并在输出上进行编码。那么这对你有什么用呢? 包装Redis驱动程序,以便它返回您想要的内容,从而将解码推回到代码的外围。

例如,它应该如下:

class UnicodeRedis(redis.Redis):

    def __init__(self, *args, **kwargs):
        if "encoding" in kwargs:
            self.encoding = kwargs["encoding"]
        else:
            self.encoding = "utf-8"
        super(UnicodeRedis, self).__init__(*args, **kwargs)

    def get(self, *args, **kwargs):
        result = super(UnicodeRedis, self).get(*args, **kwargs)
        if isinstance(result, str):
            return result.decode(self.encoding)
        else:
            return result

然后您可以正常与它进行交互,除了您可以传递更改字符串解码方式的encoding参数。如果您未设置encoding,则此代码将采用utf-8

E.g。

r_server = UnicodeRedis('localhost')
city = r_server.get('city')

<击>

答案 1 :(得分:0)

正如J.F. Sebastian所说,redis-py API支持通过在redis.Redis类的 init 方法中设置decode_response=True来解码对unicode的响应。