改善Python程序-Unicode和编码

字符串和编码

  • Unicode把所有语言都统一到一套编码里,用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
  • 在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件
  • 浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器,很多网页的源码上会有类似<meta charset="UTF-8" />的信息,表示该网页正是用的UTF-8编码
  • 由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes;如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法
1
2
3
4
5
6
7
8
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

1
2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

Unicode和编码

  • (1)最早的ASCII编码用一个字节(8bit,最高位为0)只能表示128个字符
  • (2)为不同的文字分配统一编码,Unicode为每种语言设置了唯一的二进制编码表示方式
  • (3)Unicode编码系统可以分为编码方式和实现方式
  • (4)在编码方式上,分为UCS-2和UCS-4两种方式,UCS-2用两个字节编码,UCS-4用4个字节编码。
  • (5)Unicode的实现方式称为Unicode转换格式,简称为UTF,包括UTF-7、UTF-16、UTF-32,UTF-8等,其中较为常见的为UTF-8。UTF-8的特点是对不同范围的字符使用不同长度的编码,其中0x00~0x7F的字符的UTF-8编码与ASCII编码完全相同。

读出文件的内容显示为乱码

1
2
3
4
# 其中,文件test.txt中的内容为“python中文测试”,文件以UTF-8的形式保存,结果乱码
filehandle = open("test.txt",'r')
print filehandle.read()
filehandle.close()

读入的文件test.txt用UTF-8编码形式保存,但是Windows的本地默认编码是CP936,在Windows系统中它被映射为GBK编码,所以当在控制台上直接显示UTF-8字符的时候,这两种编码并不兼容,以UTF-8形式表示的编码在GBK编码中被解释成为其他的符号,由此便产生了乱码

1
2
3
4
# 修改
filehandle = open("test.txt",'r')
print (filehandle.read().decode("utf-8")).encode("gbk")
print (filehandle.read().decode("utf-8")).encode("gbk")

分别使用了decode()和encode()方法,这两个方法的作用分别是对字符串进行解码和编码。其中decode()方法将其他编码对应的字符串解码成Unicode,而encode()方法将Unicode编码转换为另一种编码,Unicode作为转换过程中的中间编码。

1
2
str.decode([编码参数[,错误处理]])
str.encode([编码参数[,错误处理]])

Python源文件中包含中文字符的时候抛出SyntaxError异常

1
2
3
4
5
6
# bunicodetest.py文件的内容如下:
s = "python 中文测试"
print s
#上述程序运行时报错如下:
File "unicodetest.py", line 1
SyntaxError: Non-ASCII character '\xd6' in file unicodetest.py on line 1, but no

Python中默认的编码是ASCII编码,所以unicodetest.py文件是以ASCII形式保存的,s是包含中文字符的普通字符串.当调用print方法输出的时候会隐式地进行从ASCII到系统默认编码(Windows上为CP936)的转换,中文字符并不是ASCII字符.

1
2
3
4
5
6
7
8
9
10
11
12
第一种声明方式:
# coding=<encoding name>
第二种声明方式:
#!/usr/bin/python
# -*- coding: <encoding name> -*-
第三种声明方式:
#!/usr/bin/python
# vim: set fileencoding=<encoding name> :
示例二在源文件头中加入编码声明# coding=utf-8便可解决问题。
普通字符和Unicode进行字符串连
1
2
3
4
5
6
7
# coding=utf-8
s = "中文测试"+ u"Chinese Test"
print s
# 程序运行抛出异常如下:
Traceback (most recent call last):
File "tests.py", line 2, in <module>
s = "中文测试"+ u"Chinese Test"

使用+操作符来进行字符串的连接时,+左边为中文字符串,类型为str,右边为Unicode字符串。当两种类型的字符串连接的时候,Python将左边的中文字符串转换为Unicode再与右边的Unicode字符串做连接,将str转换为Unicode时使用系统默认的ASCII编码对字符串进行解码,但由于“中文测试”的ASCII编码为\xd6\xd0\xce\xc4\xb2\xe2\xca\xd4,其中“中”字的编码\xd6对应的值为214。当编码值在0~127的时候Unicode和ASCII是兼容的,转换不会有什么问题,但当其值大于128的时候,ASCII编码便不能正确处理这种情况,因而抛出UnicodeDecodeError异常。

1
2
3
4
5
6
# 1)指定str转为Unicode时的编码方式。
# coding=utf-8
s = "中文测试".decode('gbk') + u"Chinese Test"
# 2)将Unicode字符串进行UTF-8编码。
s = "中文测试"+ u"Chinese Test".encode("utf-8")