热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

了解Python编码和Unicode

我确定有很多关于Unicode和Python的说明,但为了方便自己的理解使用,我还是打算再写一些关于它们的东西。字节流vsUnicode对象我们先来用Python定义一个字符串。当你使用string类型时,实际上会储存一个字节串。[a][b][c]"abc"[97][98][99]"abc"在这个例子里,abc这个字符串是一个字节串。97.,98,,99是ASCII码。在Pytho
  我确定有很多关于Unicode和Python的说明,但为了方便自己的理解使用,我还是打算再写一些关于它们的东西。

  字节流 vs Unicode对象

  我们先来用Python定义一个字符串。当你使用string类型时,实际上会储存一个字节串。

[  a ][  b ][  c ] = "abc"
[ 97 ][ 98 ][ 99 ] = "abc"

  在这个例子里,abc这个字符串是一个字节串。97.,98,,99是ASCII码。在Python 2.x里定义就是将所有的字符串当做ASCII来对待。不幸的是,ASCII在拉丁式字符集里是最不常见的标准。

  ASCII是用前127个数字来做字符映射。像windows-1252和UTF-8这样的字符映射有相同的前127个字符。在你的字符串里每个字节的值低于127的时候是安全的混合字符串编码。然而作这个假设是件很危险的事情,下面还将会提到。

  当你的字符串里有字节的值大于126的时候就会有问题冒出来。我们来看一个用windows-1252编码的字符串。Windows-1252里的字符映射是8位的字符映射,那么总共就会有256个字符。前127个跟ASCII是一样的,接下来的127个是由windows-1252定义的其他字符。

A windows-1252 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 150 ] = "abc–"

  Windows-1252仍然是一个字节串,但你有没有看到最后一个字节的值是大于126的。如果Python试着用默认的ASCII标准来解码这个字节流,它就会报错。我们来看当Python解码这个字符串的时候会发生什么:

>>> x = "abc" + chr(150)
>>> print repr(x)
'abc\x96'
>>> u"Hello" + x
Traceback (most recent call last):
  File "", line 1, in ?
UnicodeDecodeError: 'ASCII' codec can't decode byte 0x96 in position 3: ordinal not in range(128)

  我们来用UTF-8来编码另一个字符串:

A UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"
[0x61] [0x62] [0x63] [0xe2]  [ 0x80] [ 0x93] = "abc-"

  如果你拿起看你熟悉的Unicode编码表,你会发现英文的破折号对应的Unicode编码点为8211(0×2013)。这个值大于ASCII最大值127。大于一个字节能够存储的值。因为8211(0×2013)是两个字节,UTF-8必须利用一些技巧告诉系统存储一个字符需要三个字节。我们再来看当Python准备用默认的ASCII来编码一个里面有字符的值大于126的UTF-8编码字符串。

>>> x = "abc\xe2\x80\x93"
>>> print repr(x)
'abc\xe2\x80\x93'
>>> u"Hello" + x
Traceback (most recent call last):
  File "", line 1, in ?
UnicodeDecodeError: 'ASCII' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)

  你可以看到,Python一直是默认使用ASCII编码。当它处理第4个字符的时候,因为它的值为226大于126,所以Python抛出了错误。这就是混合编码所带来的问题。

  解码字节流

  在一开始学习Python Unicode 的时候,解码这个术语可能会让人很疑惑。你可以把字节流解码成一个Unicode对象,把一个Unicode 对象编码为字节流。

  Python需要知道如何将字节流解码为Unicode对象。当你拿到一个字节流,你调用它的“解码方法来从它创建出一个Unicode对象。

  你最好是尽早的将字节流解码为Unicode。

>>> x = "abc\xe2\x80\x93"
>>> x = x.decode("utf-8")
>>> print type(x)

>>> y = "abc" + chr(150)
>>> y = y.decode("windows-1252")
>>> print type(y)
>>> print x + y
abc–abc–

  将Unicode编码为字节流

  Unicode对象是一个文本的编码不可知论的代表。你不能简单地输出一个Unicode对象。它必须在输出前被变成一个字节串。Python会很适合做这样的工作,尽管Python将Unicode编码为字节流时默认是适用ASCII,这个默认的行为会成为很多让人头疼的问题的原因。

>>> u = u"abc\u2013"
>>> print u
Traceback (most recent call last):
  File "", line 1, in 
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2013' in position 3: ordinal not in range(128)
>>> print u.encode("utf-8")
abc–

  使用codecs模块

  codecs模块能在处理字节流的时候提供很大帮助。你可以用定义的编码来打开文件并且你从文件里读取的内容会被自动转化为Unicode对象。

  试试这个:

>>> import codecs
>>> fh = codecs.open("/tmp/utf-8.txt", "w", "utf-8")
>>> fh.write(u"\u2013")
>>> fh.close()

  它所做的就是拿到一个Unicode对象然后将它以utf-8编码写入到文件。你也可以在其他的情况下这么使用它。

  试试这个:

  当从一个文件读取数据的时候,codecs.open 会创建一个文件对象能够自动将utf-8编码文件转化为一个Unicode对象。

  我们接着上面的例子,这次使用urllib流。

>>> stream = urllib.urlopen("http://www.google.com")
>>> Reader = codecs.getreader("utf-8")
>>> fh = Reader(stream)
>>> type(fh.read(1))

>>> Reader

  单行版本:

>>> fh = codecs.getreader("utf-8")(urllib.urlopen("http://www.google.com"))
>>> type(fh.read(1))

  你必须对codecs模块十分小心。你传进去的东西必须是一个Unicode对象,否则它会自动将字节流作为ASCII进行解码。

>>> x = "abc\xe2\x80\x93" # our "abc-" utf-8 string
>>> fh = codecs.open("/tmp/foo.txt", "w", "utf-8")
>>> fh.write(x)
Traceback (most recent call last):
File "", line 1, in 
File "/usr/lib/python2.5/codecs.py", line 638, in write
  return self.writer.write(data)
File "/usr/lib/python2.5/codecs.py", line 303, in write
  data, cOnsumed= self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)

  哎呦我去,Python又开始用ASCII来解码一切了。

  将UTF-8字节流切片的问题

  因为一个UTF-8编码串是一个字节列表,len( )和切片操作无法正常工作。首先用我们之前用的字符串。

[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"

  接下来做以下的:

>>> my_utf8 = "abc–"
>>> print len(my_utf8)
6

  神马?它看起来是4个字符,但是len的结果说是6。因为len计算的是字节数而不是字符数。

>>> print repr(my_utf8)
'abc\xe2\x80\x93'

  现在我们来切分这个字符串。

>>> my_utf8[-1] # Get the last char
'\x93'

  我去,切分结果是最后一字节,不是最后一个字符。

  为了正确的切分UTF-8,你最好是解码字节流创建一个Unicode对象。然后就能安全的操作和计数了。

>>> my_unicode = my_utf8.decode("utf-8")
>>> print repr(my_unicode)
u'abc\u2013'
>>> print len(my_unicode)
4
>>> print my_unicode[-1]
–

  当Python自动地编码/解码

  在一些情况下,当Python自动地使用ASCII进行编码/解码的时候会抛出错误。

  第一个案例是当它试着将Unicode和字节串合并在一起的时候。

>>> u"" + u"\u2019".encode("utf-8")
Traceback (most recent call last):
  File "", line 1, in 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0:   ordinal not in range(128)

  在合并列表的时候会发生同样的情况。Python在列表里有string和Unicode对象的时候会自动地将字节串解码为Unicode。

>>> ",".join([u"This string\u2019s unicode", u"This string\u2019s utf-8".encode("utf-8")])
Traceback (most recent call last):
  File "", line 1, in 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 11:  ordinal not in range(128)

  或者当试着格式化一个字节串的时候:

>>> "%s\n%s" % (u"This string\u2019s unicode", u"This string\u2019s  utf-8".encode("utf-8"),)
Traceback (most recent call last):
  File "", line 1, in 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 11: ordinal not in range(128)

  基本上当你把Unicode和字节串混在一起用的时候,就会导致出错。

  在这个例子里面,你创建一个utf-8文件,然后往里面添加一些Unicode对象的文本。就会报UnicodeDecodeError错误。

>>> buffer = []
>>> fh = open("utf-8-sample.txt")
>>> buffer.append(fh.read())
>>> fh.close()
>>> buffer.append(u"This string\u2019s unicode")
>>> print repr(buffer)
['This file\xe2\x80\x99s got utf-8 in it\n', u'This string\u2019s unicode']
>>> print "\n".join(buffer)
Traceback (most recent call last):
  File "", line 1, in 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 9: ordinal not in range(128)

  你可以使用codecs模块把文件作为Unicode加载来解决这个问题。

>>> import codecs
>>> buffer = []
>>> fh = open("utf-8-sample.txt", "r", "utf-8")
>>> buffer.append(fh.read())
>>> fh.close()
>>> print repr(buffer)
[u'This file\u2019s got utf-8 in it\n', u'This string\u2019s unicode']
>>> buffer.append(u"This string\u2019s unicode")
>>> print "\n".join(buffer)
This file’s got utf-8 in it

This string’s unicode

  正如你看到的,由codecs.open 创建的流在当数据被读取的时候自动地将比特串转化为Unicode。

  最佳实践

  1.最先解码,最后编码

  2.默认使用utf-8编码

  3.使用codecs和Unicode对象来简化处理

  最先解码意味着无论何时有字节流输入,需要尽早将输入解码为Unicode。这会防止出现len( )和切分utf-8字节流发生问题。

  最后编码意味着只有在准备输入的时候才进行编码。这个输出可能是一个文件,一个数据库,一个socket等等。只有在处理完成之后才编码unicode对象。最后编码也意味着,不要让Python为你编码Unicode对象。Python将会使用ASCII编码,你的程序会崩溃。

  默认使用UTF-8编码意味着:因为UTF-8可以处理任何Unicode字符,所以你最好用它来替代windows-1252和ASCII。

  codecs模块能够让我们在处理诸如文件或socket这样的流的时候能少踩一些坑。如果没有codecs提供的这个工具,你就必须将文件内容读取为字节流,然后将这个字节流解码为Unicode对象。

  codecs模块能够让你快速的将字节流转化为Unicode对象,省去很多麻烦。

  解释UTF-8

  最后的部分是让你能入门UTF-8,如果你是个超级极客可以无视这一段。

  利用UTF-8,任何在127和255之间的字节是特别的。这些字节告诉系统这些字节是多字节序列的一部分。

Our UTF-8 encoded string looks like this:
[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = "abc–"

  最后3字节是一个UTF-8多字节序列。如果你把这三个字节里的第一个转化为2进制可以看到以下的结果:

11100010

  前3比特告诉系统它开始了一个3字节序列226,128,147。

  那么完整的字节序列。

11100010 10000000 10010011

  然后你运用三字节序列的下面的掩码。

1110xxxx 10xxxxxx 10xxxxxx
XXXX0010 XX000000 XX010011 Remove the X's
0010       000000   010011 Collapse the numbers
00100000 00010011          Get Unicode number 0x2013, 8211 The "–"

  这是基本的UTF-8入门,如果想知道更多的细节,可以去看UTF-8的维基页面。

  原文链接: ERIC MORITZ 翻译: 伯乐在线 - 贱圣OMG

以上就是了解Python编码和Unicode的详细内容,更多请关注 第一PHP社区 其它相关文章!


推荐阅读
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
author-avatar
边怀珠大海
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有