python2和python3编码问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hjxzb/article/details/81842747

欢迎加入python学习交流群 667279387

近期有同学在群里面问编解码的问题,为啥在python2中可以到了python3中为啥不行了。其实这设计到python2和python3编码的问题。这是一篇写比较不错的文档分享:Pragmatic Unicode ,这里在原文的基础上面进行了一下整理。

一、什么是编解码

1、什么是unicode

Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

2、编码方式

字符集:为每一个字符分配一个唯一的 ID(码位 / 码点 / Code Point),编码规则:将码位转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)。为了将unicode字符转成bytes,需要进行编码,常见的编码方案有:UTF-16, UTF-32, UCS-2, UCS-4, UTF-8

二、python中的编解码

1、python2

1、str: 是比特序列

2、unicode: code points序列

>>> my_string = "Hello World"
>>> type(my_string)
<type 'str'>

>>> my_unicode = u"Hi \u2119\u01b4\u2602\u210c\xf8\u1f24"
>>> type(my_unicode)
<type 'unicode'>

(1).encode() 和 .decode()

unicode .encode() → bytes //encode函数就是将unicode转换成bytes

bytes .decode() → unicode //对应的解码过程就是将bytes转成unicode

>>> my_unicode = u"Hi \u2119\u01b4\u2602\u210c\xf8\u1f24"
>>> len(my_unicode)
9

>>> my_utf8 = my_unicode.encode('utf-8')
>>> len(my_utf8)
19
>>> my_utf8
'Hi \xe2\x84\x99\xc6\xb4\xe2\x98\x82\xe2\x84\x8c\xc3\xb8\xe1\xbc\xa4'

>>> my_utf8.decode('utf-8')
u'Hi \u2119\u01b4\u2602\u210c\xf8\u1f24'

更多请参考官方教程:https://docs.python.org/2/library/stdtypes.html?highlight=decode#str.decode

(2)编解码错误和处理

许多编码方案并能将全部的unicode字符进行编码。

>>> my_unicode.encode('ascii')
Traceback (most recent call last):
UnicodeEncodeError: 'ascii' codec can't encode characters in
          position 3-8: ordinal not in range(128)

同样反过来,也并不是每一个str都是合法的unicode编码。

>>> my_utf8.decode("ascii")
Traceback (most recent call last):
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in
          position 3: ordinal not in range(128)

>>> "\x78\x9a\xbc\xde\xf0".decode("utf-8")
Traceback (most recent call last):
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x9a in
          position 1: invalid start byte

这是官网给出来的一个在不能完全编码采用哪种方案进行处理的示例。

>>> unicode('\x80abc', errors='strict')     
Traceback (most recent call last):
    ...
UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0:
ordinal not in range(128)
>>> unicode('\x80abc', errors='replace')
u'\ufffdabc'
>>> unicode('\x80abc', errors='ignore')
u'abc'

(3)令人抓狂的隐式转换

下面就是python2中比较用起来爽,但是有时又让你很抓狂的东西—隐式转换。看下面这个例子:

>>> u"Hello " + "world"
u'Hello world'

>>> u"Hello " + ("world".decode("ascii"))
u'Hello world'

>>> sys.getdefaultencoding()
'ascii'

你可以将str和unicode进行拼接,语法解析器会自动给你做隐式转换。当然在很多时候是可以很好完成你的工作的,但是有时却不好使:

>>> u"Hello " + my_utf8
Traceback (most recent call last):
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in
          position 3: ordinal not in range(128)

>>> u"Hello " + (my_utf8.decode("ascii"))
Traceback (most recent call last):
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in
          position 3: ordinal not in range(128)

2、python3

这里将python2和python3做了一个对比放在下面的表格里面了。

—— python2 python3
str a sequence of bytes a sequence of code points (unicode)
unicode a sequence of code points (unicode) ——
bytes —— a sequence of bytes

也就是说在python中,字符串为unicode格式的码位。下面看看在python3中的示例

>>> my_string = "Hi \u2119\u01b4\u2602\u210c\xf8\u1f24"
>>> type(my_string)
<class 'str'>

>>> my_bytes = b"Hello World"
>>> type(my_bytes)
<class 'bytes'>

(1)encode和decode

在python3中,字符串str是unicode的,你可以利用encode函数将其转成其他的编码格式。

>>> nonlat = '字' //unicode格式
>>> nonlat
'字'
>>> print(nonlat)
字
>>> bytes(nonlat) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: string argument without an encoding

>>> bytes(nonlat, 'utf-8')//和下面直接调用encode方法的区别可以参考下面给出的Stack Overflow的链接 
b'\xe5\xad\x97'

>>> nonlat.encode() //encode函数默认使用utf-8编码
b'\xe5\xad\x97'

>>> nonlat.encode('utf-16') 
b'\xff\xfeW['

>>> b'\xff\xfeW['.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

>>> b'\xff\xfeW['.decode('utf-16')
''

区别:https://stackoverflow.com/questions/14472650/python-3-encode-decode-vs-bytes-str

更多请参考官方文档:https://docs.python.org/3/library/stdtypes.html?highlight=decode#bytes.decode

(2)无隐式转换

在python3中没有了python2中的隐式转换。你必须两种格式分开来处理。

>>> "Hello " + b"world"
Traceback (most recent call last):
TypeError: Can't convert 'bytes' object to str implicitly

>>> "Hello" == b"Hello"
False

>>> d = {"Hello": "world"}
>>> d[b"Hello"]
Traceback (most recent call last):
KeyError: b'Hello'

例如读取文件的时候,你就必须要注意格式的问题:

>>> open("hello.txt", "r").read()
'Hello, world!\n'

>>> open("hello.txt", "rb").read()
b'Hello, world!\n'

>>> open("hi_utf8.txt", "r").read()
'Hi \xe2\u201e\u2122\xc6\xb4\xe2\u02dc\u201a\xe2\u201e\u0152\xc3\xb8\xe1\xbc\xa4'

>>> open("hi_utf8.txt", "r", 
...     encoding=locale.getpreferredencoding()).read()
'Hi \xe2\u201e\u2122\xc6\xb4\xe2\u02dc\u201a\xe2\u201e\u0152\xc3\xb8\xe1\xbc\xa4'

>>> open("hi_utf8.txt", "r", encoding="utf-8").read()
'Hi \u2119\u01b4\u2602\u210c\xf8\u1f24'

>>> open("hi_utf8.txt", "rb").read()
b'Hi \xe2\x84\x99\xc6\xb4\xe2\x98\x82\xe2\x84\x8c\xc3\xb8\xe1\xbc\xa4

(3)编程注意点

1、Unicode sandwich

Bytes on the outside, unicode on the inside Encode/decode at the edges

这里写图片描述
2、Know what you have

Bytes or Unicode? If bytes, what encoding?

>>> print type(my_unicode)
<type 'unicode'>

>>> print repr(my_unicode)
u'Hi \u2119\u01b4\u2602\u210c\xf8\u1f24'

参考资料:

1、https://en.wikipedia.org/wiki/Unicode

2、https://docs.python.org/2/howto/unicode.html

3、https://stackoverflow.com/questions/14472650/python-3-encode-decode-vs-bytes-str

4、https://docs.python.org/3/library/stdtypes.html?highlight=decode#bytes.decode

欢迎加入python学习交流群 667279387

猜你喜欢

转载自blog.csdn.net/hjxzb/article/details/81842747