ZEROMAKE | keep codeing and thinking!
2018-05-04 | python

python 编码

前言

  • 最近对 python 的编码感觉到很迷,python2python3 的 str bytes 完全不一样。
  • 这边记录下各种方式输入的字符串编码,以及各种编码对象的转换备忘。

一、input 函数在各个平台和 python 版本中的变化

1.1 测试代码

1
# -*- coding=utf8 -*-
2
import sys
3
4
IS_PY3 = sys.version_info.major == 3
5
6
default_coding = "utf8"
7
8
def print_str(test):
9
print("input str: ", test)
10
print("input repr: ", repr(test))
11
print("input type:", type(test))
12
if hasattr(test, "decode"):
13
try:
14
temp1 = test.decode(default_coding)
15
print("has decode:", temp1, type(temp1))
16
except (UnicodeEncodeError, UnicodeDecodeError) as e:
17
print(e)
18
if hasattr(test, "encode"):
19
try:
20
temp2 = test.encode(default_coding)
21
print("has encode:", temp2, type(temp2))
22
except (UnicodeEncodeError, UnicodeDecodeError) as e:
23
print(e)
24
25
def test_input():
26
if IS_PY3:
27
name = input("test str: ")
28
else:
29
name = raw_input("test str: ")
30
# print(name)
31
print_str(name)
32
33
if __name__ == "__main__":
34
test_input()
35

1.2. UNIX 下

python2

1
$ python test.py
2
test str: 测试
3
('input str: ', '\xe6\xb5\x8b\xe8\xaf\x95')
4
('input repr: ', "'\\xe6\\xb5\\x8b\\xe8\\xaf\\x95'")
5
('input type:', <type 'str'>)
6
('has decode:', u'\u6d4b\u8bd5', <type 'unicode'>)
7
Traceback (most recent call last):
8
File "test.py", line 32, in <module>
9
test_input()
10
File "test.py", line 29, in test_input
11
print_str(name)
12
File "test.py", line 18, in print_str
13
temp2 = test.encode("utf8")
14
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)

总结

  1. python2raw_input 函数把控制台输入的 bytes 直接拿来了,编码为控制台的指定编码(utf8)。
  2. python2 的 <type 'str'> 就是一个 bytes 类型, 通过 decode 可以把 str(bytes) 转换为 unicode(str)
  3. 至于有 encode 不能转换就是因为本身就是 str(bytes) 无法在转换。还有就是设计问题,否则 python3 就不用改了。

python3

1
$ python3 test.py
2
test str: 测试
3
input str: 测试
4
input repr: '测试'
5
input type: <class 'str'>
6
has encode: b'\xe6\xb5\x8b\xe8\xaf\x95' <class 'bytes'>

总结

  1. python3 中的 input 函数明显做了包装,会把标准输入流中的 bytes 转换为 str 也就是 python2unicode
  2. 由于本身就是 str(unicode)python3 直接去掉了 str.decode,很明显这样更符合编程思想。
  3. 这里这样的做法省去了考虑控制台编码的问题。

1.3. windows 系统

二、编写代码时的字面量

2.1. 测试说明

在代码中的字面量字符串,暂时没有发现平台区别。

2.2. python2

测试代码

1
def main():
2
print("raw")
3
print_str("测试")
4
print("unicode")
5
print_str(u"测试")
6
print("bytes")
7
print_str(b"测试")

执行结果

1
$ python test.py
2
raw
3
('input str: ', '\xe6\xb5\x8b\xe8\xaf\x95')
4
('input repr: ', "'\\xe6\\xb5\\x8b\\xe8\\xaf\\x95'")
5
('input type:', <type 'str'>)
6
('has decode:', u'\u6d4b\u8bd5', <type 'unicode'>)
7
'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
8
unicode
9
('input str: ', u'\u6d4b\u8bd5')
10
('input repr: ', "u'\\u6d4b\\u8bd5'")
11
('input type:', <type 'unicode'>)
12
'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
13
('has encode:', '\xe6\xb5\x8b\xe8\xaf\x95', <type 'str'>)
14
bytes
15
('input str: ', '\xe6\xb5\x8b\xe8\xaf\x95')
16
('input repr: ', "'\\xe6\\xb5\\x8b\\xe8\\xaf\\x95'")
17
('input type:', <type 'str'>)
18
('has decode:', u'\u6d4b\u8bd5', <type 'unicode'>)
19
'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)

总结

  1. 可以明显看出 b""""python 都是同一种类型 <type str> 也就是其它语言中的 bytes
  2. 编码为代码头申明的 #-*- coding=utf8 -*-

2.3. python3

测试代码

1
def main():
2
print("raw")
3
print_str("测试")
4
print("unicode")
5
print_str(u"测试")
6
print("bytes")
7
print_str("测试".encode("utf8"))

执行结果

1
$ python3 test.py
2
raw
3
input str: 测试
4
input repr: '测试'
5
input type: <class 'str'>
6
has encode: b'\xe6\xb5\x8b\xe8\xaf\x95' <class 'bytes'>
7
unicode
8
input str: 测试
9
input repr: '测试'
10
input type: <class 'str'>
11
has encode: b'\xe6\xb5\x8b\xe8\xaf\x95' <class 'bytes'>
12
bytes
13
input str: b'\xe6\xb5\x8b\xe8\xaf\x95'
14
input repr: b'\xe6\xb5\x8b\xe8\xaf\x95'
15
input type: <class 'bytes'>
16
has decode: 测试 <class 'str'>

总结

  1. 这里明显看出 strunicode 是同一种类型。
  2. 由于把字面量字符串转为了 unicode 不用在意头部编码申明,也不用管代码文本编码了。
  3. python3 中的字面量 bytes 只能使用 ascii 如果想使用其它的只能通过 str.encode(${代码文本编码})。