Python 2.x 中的 String 与 Unicode

转载 2014年11月09日 19:25:17

在 Python 2.x 中是有两种字串符相关类型的,分别为 String 和 Unicode,两者提供的接口非常类似,有时候又能自动转换,蛮容易误导人的。在 Python 3 中 这两个类型分别用 Bytes 和 String 替代了。这个名字更能说明两者的本质:Python 2.x 中的 String 中存储的是没有编码信息的字节序列,也就是说 String 中存储的是已经编码过后的序列,但他并不知道自身是用的哪种编码。相反的 Unicode 中存储的是记载了编码的字串信息,其中存储的就是相应字符的 Unicode 编号。在这里用程序来说明,我们建立一个简单的脚本名字为 encoding.py,代码如下:

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

strs = "这是中文"
unis = "这也是中文".decode("utf8")

print strs[0:2]
print unis[0:2].encode('gbk')

print len(strs)
print len(unis)

前面两行后面会解释到,就是限定运行环境以及该脚本文件的编码格式。此脚本在这里可以下载,如果你要自己写的话请务必确保脚本的编码是 utf8 而不是别的。在 Windows 下的运行结果在这里,我觉得正好能说明问题:

C:\SHARED\Dev\scripts>encoding.py
杩
这也
12
5

这里需要说明,我们的程序是 UTF8 编码,主要意义是该程序中的所有直接写出来的字串符(用"", ''括起来的字串符)是运用 UTF8 格式编码的;然而 Windows 下的命令行是 GBK 环境。这里 strs 是一个 String。事实上在 Python 2.x 中直接写在程序中的字串符,其类型都是 String(这里不考虑 string literal)。我们先直接输出 strs[0:2],得到的是一个乱码字符(这个字符只是碰巧凑成是一个字)。如上面说的,String 中存储的是没有编码信息的字串序列,这里就是将strs中前两个编号取出并尝试显示。由于命令行环境为 GBK 编码,这里对应的字碰巧凑成了一个字,但是跟原本的字没有任何关系。

unis 是由一个 String 调用 decode() 方法得到,这正是在 Python 2.x 中取得 Unicode 的最基本的方式。由于 String 并不知道它本身是由什么编码格式来进行的编码,这里是我们的责任来确定他原来是用哪种编码方式进行编码。我们知道代码中的编码格式是 UTF8,所以我们可以用调用 String的 decode() 方法来进行反编码,也就是解码, 把字串符从某种编码后的格式转换为其唯一对应的 Unicode 编号。unis 为解码获得的结果,其在 Python 2.x 中对应类型就是 Unicode,其中存储的就是 每个字符对应的 Unicode 编号。

我们尝试输出 unis 的前两个字符,在这里我们调用了 Unicode 的 encode() 方法。这就是编码的过程。我们知道 Windows 命令行下的编码是 GBK,只有采用 GBK 编码的字符才能正确显示。所以在这里我们通过调用 Unicode 的 encode() 方法,将 unis 中存储的 Unicode 编号 按照 GBK 的规则来进行编码,并输出到屏幕上。这里我们看到这里正确的显示了 unis 中的前两个字符。要注意的是在命令行中直接 print Unicode 的话 Python 会自动根据当前环境进行编码后再显示,但这样掩盖了两者的区别。建议总是手动调用 encode 和 decode 方法,这样自己也会清楚一些。

后面两者长度的差别也是佐证我们之前的例子。strs 中存储的是 UTF8 编码后的编号序列,上面看到一个中文字符在 UTF8 编码后变成三个连续的,所以 strs 长度为 3x4 = 12。你可以想象 strs 中存放的并不是中文,而是一系列没有意义的比特序列;而 unis 中存储的是对应的中文的 Unicode 编码。我们知道每一个字符对应一个编号,所以五个字对应五个编号,长度为 5。

避免,和解决编码产生的问题

了解了 Python Unicode 编码解码的这些概念后,我们来看看如何尽量的避免遇到让人烦心的编码问题。

首先如果你的代码中有中文,那么一定要务必声明代码的编码格式。根据 PEP-0263 中的介绍,在程序的最开始加上以下两行注释就能确定编码:

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

其中 utf-8 就是指定的编码格式。事实上你应该总是使用 UTF8 作为你 Python 程序的编码格式,因为未来的 Python 3 所有文件都将默认以 UTF8 编码。另外除了声明,你必须确定你用来编辑 Python 程序的编辑器是不是真的以 UTF8 编码来存储文件。

之后就是养成关于编码解码的好习惯。当你的程序有 String 作为输入时,应该尽早的将其转换为Unicode,再在程序中进行处理。再输出的时候,也要尽可能玩,直到最后输出的时刻才将 Unicode编码为所需编码格式的 String 进行输出。同样的你必须保持你程序内部所有参与运算的字串都是Unicode 格式。很多著名的 Python 库例如 django 就是采用的这种方式,效果也蛮好。千万不要依赖 Python 自己进行两者之间的转换,也不要将 String 和 Unicode 放在一起运算,这些行为一方面十分容易引起错误,另一方面在 Python 3 中已经无法再现。

虽说确定 String 的编码格式是程序员的责任,但有时候你真的不知道有些字串符到底是什么编码的。这里有一个神奇 chardet 能够帮助你。以下是摘自其页面上的例子,很好了说明了它的作用:读入任意一串字符,猜测其编码格式,并且给出猜测的确信度。

>>> import urllib
>>> urlread = lambda url: urllib.urlopen(url).read()
>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{'encoding': 'GB2312', 'confidence': 0.99}

>>> chardet.detect(urlread("http://yahoo.co.jp/"))
{'encoding': 'EUC-JP', 'confidence': 0.99}

>>> chardet.detect(urlread("http://amazon.co.jp/"))
{'encoding': 'SHIFT_JIS', 'confidence': 1}

>>> chardet.detect(urlread("http://pravda.ru/"))
{'encoding': 'windows-1251', 'confidence': 0.9355}

如果 confidence 非常低的话或者 chardet 直接报错,多半是字串经过多次错误编码解码,要从别的地方找办法解决问题。

在处理包含汉字的文本文件时,一个很常见的问题就是有时候会碰到带有 UTF BOM 的文件。这个简单讲就是文件头几个字节是用来表示文件是大端还是小端表示。在实际中用的很少,而且会带来很头疼的问题。有时候你确定你有一个文件是 UTF8 编码的,但读进来头几个字节就出错,那么十有八九就是这个的问题。Python 在读取文件时仍然是所有字节顺序读进来,不会透明的处理这个东西。所以要么你可以用编辑器来把文件另存为无 BOM 的,要么在 Python 中做处理。在标准库中有 codec 里面提供了相关功能:

import codecs
s = f.read(3)
if s == codecs.BOM_UTF8:
    print "BOM detected"

这样可以简单检测 BOM 是否存在,剩下的部分就要你自己发挥了。

Python: 在Unicode和普通字符串之间转换

1.1. 问题 Problem You need to deal with data that doesn't fit in the ASCII character set. 你需要处...
  • mydriverc2
  • mydriverc2
  • 2017年12月01日 14:52
  • 64

Python: 在Unicode和普通字符串之间转换

1.1. 问题Problem You need to deal with data that doesn't fit inthe ASCII character set. 你需要处理不适合...
  • u012448083
  • u012448083
  • 2016年07月15日 16:06
  • 19367

Python中unicode编码的字符串和其他格式的字符串之间进行转换

1.1. 问题 Problem You need to deal with data that doesn't fit in the ASCII character set. 你需要处...
  • nyist327
  • nyist327
  • 2015年10月09日 19:47
  • 1807

Unicode环境下完成CString向string类型的转换

CString是MFC的字符串类,它不是基本类型,而是对字符串的封装,它是自适应的,在UNICODE环境下就是CStringW,在非UNICODE环境下就是CStringA。       CStri...
  • lishuhuakai
  • lishuhuakai
  • 2014年06月04日 10:47
  • 5353

C++字符串之间转化——Unicode字符集

本文主要介绍:Unicode字符集下字符串之间以及字符串与数字之间的转化。 1.string、char*与 const char* string->char*char *ctr = new cha...
  • hong__fang
  • hong__fang
  • 2015年07月03日 17:12
  • 1867

深入理解Python的字符编码utf-8 & unicode

在Python中有两个和字符很相关的类型,一个是str类型,一个是unicode类型。 这两种类型的对象都是sequece序列,其中str是字节序列,而unicode是字符序列在2.x版本的pyth...
  • warrior_zhang
  • warrior_zhang
  • 2016年01月12日 13:03
  • 4013

Java实现字符与Unicode互转

Java实现字符与Unicode互转,有两个主要方法: Integer.toHexString();//转码 Integer.parseInt();//解码 通过以上两个方法实现对字符的转码与...
  • u012500848
  • u012500848
  • 2017年04月01日 17:52
  • 1565

python学习之unicode编码

python内建的字符串有两种类型:str和Unicode,它们拥有共同的祖先basestring。 Unicode也称做万国码,它为每种语言设定了唯一的二进制编码表示方式,提供从数字代码到...
  • u010867294
  • u010867294
  • 2016年06月30日 15:26
  • 318

Python中的str与unicode处理方法

转自:http://python.jobbole.com/81244/ python2.x中处理中文,是一件头疼的事情。网上写这方面的文章,测次不齐,而且都会有点错误,所以在这里打算自己总结一篇...
  • vickyrocker1
  • vickyrocker1
  • 2016年03月22日 08:58
  • 5524

在Python中正确使用Unicode

正确处理文本,特别是正确处理Unicode。是个老生常谈的问题,有时甚至会难倒经验丰富的开发者。并不是因为这个问题很难,而是因为对软件中的文本,开发者没有正确理解一些关键概念及其表示方法。在Stack...
  • yixiaojiejie
  • yixiaojiejie
  • 2015年01月22日 18:56
  • 647
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Python 2.x 中的 String 与 Unicode
举报原因:
原因补充:

(最多只允许输入30个字)