python字符串成熟编码_Python 2 字符串编码踩坑小结

Tips: 如果您已经充分理解问题是什么,请直接跳到 #问题出在哪里 一节。

字符串和编码

先从概念说起,字符串和它的编码是两个不同的概念:

字符串是一段文字本身,可以是中文可以是英文,以及各种语言

字符串的编码是计算机存储、处理字符串的方式;作为一种数据,它和其他数据一样,都是以一串0和1组成的,通常我们用字节数组来表示它。

字符串经过编码(encode) 就成为了一堆数据,反过来,数据经过解码(decode) 就变回我们认识的字符串。

从 Python 3 说起

这个编码问题(坑)可以说是 Python 2 被吐槽最多的黑点,没有之一。为了防止上来就掉进 Python 2 的坑里,我们先来看看 Python 3 里“改进”后是什么样子的。

1

2

3

4

5>>>s = "Hello, 世界"

>>>type(s)

>>> len(s)

9

哈,没有任何问题!(数长度的时候别漏了空格)

查阅文档,我们发现 str 有个函数叫 encode(),它看起来很眼熟,让我们来试试:

1

2

3>>>b = s.encode("utf-8")

>>>b

b'Hello, \xe4\xb8\x96\xe7\x95\x8c'

这个 b'' 的前缀表示返回值是一个 bytes 变量,也就是一堆数据了。

为什么这里面"Hello"还是原来的样子,但是“世界”变成一坨 \x?? 了?

这是因为 ASCII 实在太有名了,程序员们都看得懂:这个 H 其实表示的是一字节 0x48。而后面“世界”的编码不在 ASCII 的编码范围内,所以只能用 \x?? 表示了。

这样看起来也许更清晰一些:

1

2

3> >>> b.hex()

> '48656c6c6f2c20e4b896e7958c'

>

有 encode() 当然也有 decode()。我们对刚刚拿到的 bytes b 解码,果然会变成原来的字符串。

1

2>>>b.decode('utf-8')

'Hello, 世界'

OK,现在你已经明白了奥义所在,是时候去踩坑了。

Python 2 的世界

初见茅庐

先来一道开胃菜:

1

2

3

4

5>>>s = "Hello, 世界"

>>>type(s)

>>>len(s)

13

▲ 为什么这个长度是 13 ?明明是 9 个字符啊!

1

2>>>s

'Hello, \xe4\xb8\x96\xe7\x95\x8c'

▲ s 你怎么坏掉了?

1

2

3>>>b = s.encode('utf-8')

>>>b

'Hello, \xe4\xb8\x96\xe7\x95\x8c'

▲ 我可能用了假的 encode()

1

2>>>b.decode('utf-8')

u'Hello, \u4e16\u754c'

▲ 喵喵喵?

以上,Python 2 中字符串并不像我们想的那样工作。

问题出在哪里

其实说起来也简单,Python 是一门诞生于 1989 年的古老语言,比 Unicode 还要早两年,当时的程序员并不在乎编码问题,因为 ASCII 已经足够了。

如果你熟悉 C/C++ 会发现同样的问题:char* 被同时用于表示字符串和字节数组。Python 2 里也是同样,str 其实是个字节数组,却被挂上了字符串的名字。二十年后用着中文字符的我们被坑惨了。

后来 Python 2 为了支持 Unicode,增加了 unicode 类型,然而并没有卵用——程序员们不记得在每个字符串前面加上 u,这也不够优雅。

Python 3 设计之初就立志解决这个问题,不惜彻底修改了str的定义,把 str 这个名字让给了原来的 unicode!,而新增的 bytes 类型才是字节数组。如下表所示:

Python 2

Python 3

字符串(Unicode)

unicode

str

字节数组

str (bytes)

bytes

所以,在 Python 2 里,如果遇到非英语字符,一定要记得用 unicode。效果是这样的:

1

2

3

4

5

6

7

8

9

10

11

12>>>s = u'Hello, 世界'

>>>s

u'Hello, \u4e16\u754c'

>>>type(s)

>>>len(s)

9

>>>b = s.encode('utf-8')

>>>b

'Hello, \xe4\xb8\x96\xe7\x95\x8c'

>>>b.decode('utf-8')

u'Hello, \u4e16\u754c'

至于为什么 str 也有 encode(),主要是为了尽可能保持和 Python 3 的兼容性,以让部分程序能在 2、3 同时运行。于是事情变得更糟糕了。

原来如此

现在我们可以解释刚刚遇到的奇怪情况了:

“为什么这个长度是 13 ?明明是 9 个字符啊!”——因为 Python 自动帮你编码了,编码后是 13 个字节,常见的汉字在 UTF-8 编码下为 3 个字节

“s 你怎么坏掉了?” ——str 本来就是字节数组

“我可能用了假的 encode()”——你不应该对 str 变量做 encode,它本来就是编码后的

“喵喵喵?”——这是正常的,只是因为 Python 2 没有把 Unicode 字符显示成中文字符,用 print 就没问题了:

1

2>>>print s

Hello, 世界

解决方案

永远记住 str 其实是 bytes,字符串应该用 unicode,尤其是包含中文时

如果能说服你的老板和同事,尽快把 Python 2 升级到 3

最后,如果你需要写出兼容 Python 2\3 的程序,这篇文档可以给你一些帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值