Python之字符串和编码

字符编码

字符串相对于其他数据类型有一个特殊的地方:就是编码问题。
我们知道计算机只能处理数字,所以要让它能够处理文本就需要将文本用对应的数字来表示才能处理。所以美国人一开始设计出了ASCII编码,只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号。但是用一个字节来处理中文远远不够,至少需要两个字节,中国人只好自己制定了GB2312编码来处理汉字。由于GB2312编码能够处理的汉字也有限,微软又在GB2312编码基础上制定GBK编码。随后中国制定出了GB10803编码,与GB2312完全兼容,与GBK基本兼容,支持Unicode的全部统一汉字,共收录汉字70244个。GB 18030主要有以下特点:

  • 与UTF-8相同,采用多字节编码,每个字可以由1个、2个或4个字节组成。
  • 编码空间庞大,最多可定义161万个字符。
  • 支持中国国内少数民族的文字,不需要动用造字区。
  • 汉字收录范围包含繁体汉字以及日韩汉字。

像天朝一样,当计算机传到世界各个国家时,为了适合当地语言和字符,设计和实现类似GB2312/GBK/GB18030的编码方案。这样各搞一套,在本地使用没有问题,一旦出现在网络中,由于不兼容,互相访问就出现了乱码现象。 为了能够处理各国的文字,由此Unicode(万国码,统一码,单一码,标准万国码)编码应运而生,Unicode编码系统为表达任意语言的任意字符而设计。它使用4字节的数字来表达每个字母、符号,或者表意文字。
统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。人们又想出了把Unicode编码转化为“可变长编码”的UTF-8编码。
UTF-8使用一至四个字节为每个字符编码,UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串,所以现存的ASCII文本不需要转换。

Python字符串

几个常用关于字符串的函数:

  • ord():获取字符的整数表示。
>>> ord('b')
98
>>> ord('字')
23383
  • chr():把编码转换为对应的字符。
>>> chr(99)
'c'
>>> chr(23384)
'存'
  • encode():将字符串编码为指定的bytes。
encode()函数.png

纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。含有中文的str无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。在bytes中,无法显示为ASCII字符的字节,用\x##显示。

  • decode():若我们从网络或磁盘上读取了字节流,那么获得的数据就是bytes。要把bytes变为str,就需要用到decode()方法。
decode()函数.png

如果bytes中包含无法解码的字节,decode()方法会报错:

decode()函数报错.png

如果bytes中只有一小部分无效的字节,可以传入errors='ignore'忽略错误的字节:

ignore.png
  • len():计算str的字符数,如果换成bytes,len()函数就计算字节数。
len()函数.png

通过len()的计算我们发现:1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。
在字符串处理中,我们会经常遇到str和bytes的互相转换。为了避免乱码问题,应当坚持使用UTF-8编码对str和bytes进行转换的好习惯。

由于Python源代码也是一个文本文件,当我们的源代码中包含中文的时候,如果不用指定的编码保存源代码,当Python解释器读取源代码时,展现在我们眼前的就可能是乱码。所以我们务必要用UTF-8编码保存源代码,当Python解释器读取源代码时让它按UTF-8编码读取。因此我们通常在文件开头写上这两行:

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

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码。
申明了UTF-8编码并不代表我们的.py文件就是UTF-8编码的,必须要确保文本编辑器正在使用UTF-8 without BOM编码:

文本编辑器.png

如果.py文件本身使用UTF-8编码,并且也申明了# -- coding: utf-8 --,打开命令提示符测试就可以正常显示中文:

显示中文.png

字符串的格式化

最后一个常见的问题是如何输出格式化的字符串。我们经常会输出类似'x年x班的xxx,今年xx岁,数学期末成绩是xx'之类的字符串,而xxx的内容都是根据变量变化的,所以,需要一种简便的格式化字符串的方式。

在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:

格式化.png

%运算符就是用来格式化字符串的。在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。

常见的占位符有:

占位符 替换内容
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数

其中,格式化整数和浮点数还可以指定是否补0和整数与小数的位数:

# -*- coding: utf-8 -*-
print('%2d-%02d' % (3, 1))
print('%.2f' % 3.1415926)

如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串:

>>> 'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'

有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%:

>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'
  • format()

另一种格式化字符串的方法是使用字符串的format()方法,它会用传入的参数依次替换字符串内的占位符{0}、{1}……,不过这种方式写起来比%要麻烦得多:

>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'

小结

Python 3的字符串使用Unicode,直接支持多语言。
当str和bytes互相转换时,需要指定编码。最常用的编码是UTF-8。Python当然也支持其他编码方式,比如把Unicode编码成GB2312:

>>> '中文'.encode('gb2312')
b'\xd6\xd0\xce\xc4'

但这种方式纯属自找麻烦,如果没有特殊业务要求,请牢记仅使用UTF-8编码。
格式化字符串的时候,可以用Python的交互式环境测试,方便快捷。

你可能感兴趣的:(Python之字符串和编码)