【玩转python】一 python字符串编码

字符编码

unicode编码方式把所有语言统一成一套编码方式。一般使用两个字节表示一个字节,非常偏僻的就需要4个字节。目前大多数操作系统和编程语言都直接使用Unicode编码方式。

为了节省空间,一种‘可变长编码’的方式诞生了,他就是utf-8。utf-8编码把一个Unicode字符编码成1-6个字节,常用的字母为一个字节,汉字通常为3个字节,生僻字才会编码成4-6个字节。如果要传输大量英文字母的文本,两者相比utf-8就节省空间。

在计算机内存中,统一使用的是unicode,当需要保持到硬盘或者需要传输的时候,就会转化为utf-8。

用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里, 编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:

浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:

所以你看到很多网页的源码上会有类似的信息,表示该网页正是用的UTF-8编码

Python的字符串

python2与python3的是有差别的:

python2中表示unicode字符串需使用u'xxx',python3中都统一成unicode。而表示字节形式必须加上前缀b'xxx'。

由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输, 或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

以Unicode表示的str通过encode()方法可以编码为指定的bytes,例如:

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)

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

反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法:

1
2
3
4
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'

 

在操作字符串时,我们经常遇到str和bytes的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。

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

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

 

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;

第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码: 

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

Python2和3的差异

先准备两张图片

第一张图片是几个特殊字符的unicode代码点(CODE POINT), 用\uXXXX或者\xXX来表示,其中X都是十六进制字符,而\x表示前面一个字节为00就变成简写了。

第二张图片是UTF-8怎样来通过可变字节来编码相应字符的代码点

在python2中

  • str: a sequence of bytes

  • unicode: a sequence of code points

在 Python2 中,有两种字符串数据类型。一种纯旧式的文字: “str” 对象,存储 bytes 。如果你使用一个 “u” 前缀,那么你会有一个 “unicode” 对象,存储的是 code points 。在一个 unicode 字符串中,你可以使用\u或\x来插入任何的 unicode 代码点(\x后面接2位的十六进制表示\u00XX这样的简写)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
my_string = "Hello World."
print(type(my_string))

my_unicdoe = u"Hi \u2119\u01b4\u2602\u210c\xf8\u1f24"
print(len(my_unicode))  # 9个字符

my_utf8 = my_unicode.encode('utf-8')
print(len(my_utf8))  # 19个字节

my_unicode2 = my_utf8.decode('utf-8')
print(len(my_unicode2))  # 9个字符,恢复原来的

# my_unicode2.encode('ascii')  # 报错,因为ascii只能表示0-127个字符

my_unicode2.encode('ascii', 'replace') # 用?代替不能编码的字符
my_unicode2.encode('ascii', 'xmlcharrefreplace') # 对于不能编码的产生一个完全替代的 HTML/XML 字符,保护所有的原始数据
my_unicode2.encode('ascii', 'ignore') # 忽略不能编码的字符,留下的都是ascii字符

在python3中

  • str: a sequence of code points

  • bytes: a sequence of bytes

  1. 只有一个文本类型是str(默认就是unicode编码字符)

  2. 有两个字节类型是bytesbytearray

在 Python 2 中的 “str” 现在叫做 “bytes”,而 Python 2 中的 “unicode” 现在叫做 “str”。

1
2
3
4
5
my_string = "Hi \u2119\u01b4\u2602\u210c\xf8\u1f24"
print(type(my_string))  # <class 'str'>

my_bytes = b"Hello world"
print(type(my_bytes))  # <class 'bytes'>

避免bytes和unicode混合使用报错的原则是:制造一个 Unicode 三明治, bytes 在外, Unicode 在内。

我们有五个不可忽视的事实:

  • 程序中所有的输入和输出均为 byte

  • 世界上的文本需要比 256 更多的符号来表现

  • 你的程序必须处理 byte 和 unicode

  • byte 流中不会包含编码信息

  • 指明的编码有可能是错误的

常见乱码问题

  1. SyntaxError: Non-ASCII character

  2. UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5 in position 108

  3. UnicodeEncodeError: ‘gb2312’ codec can’t encode character u’\u2014’ in position 72366:

字符串在Python内部的表示是unicode 编码,因此,在做编码转换时,通常需要以unicode作为中间编码, 即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。

decode的作用是将其他编码的字符串转换成unicode编码,如str1.decode(‘gb2312’),表示将gb2312编码的字符串str1转换成unicode编码。encode的作用是将unicode编码转换成其他编码的字符串,如str2.encode(‘gb2312’),表示将unicode编码的字符串str2转换成gb2312编码。

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
#coding=utf-8
s="中文"
if isinstance(s, unicode):
    #s=u"中文"
    print s.encode('gb2312')
else:
    #s="中文"
    print s.decode('utf-8').encode('gb2312')

 

这是你在编程中保持 Unicode 清洁的三个建议:

  1. Unicode 三明治:尽可能的让你程序处理的文本都为 Unicode 。

  2. 了解你的字符串。你应该知道你的程序中,哪些是 unicode, 哪些是 byte, 对于这些 byte 串,你应该知道,他们的编码是什么。

  3. 测试 Unicode 支持。使用一些奇怪的符号来测试你是否已经做到了以上几点。

如果你遵循以上建议的话,你将会写出对 Unicode 支持很好的代码。不管 Unicode 中有多么不规整的编码你的程序也不会挂掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值