对于很多接触 Python 的人而言,字符的处理和语言整体的温顺可靠相比显得格外桀骜不驯难以驾驭。
本文不谈复杂的理论,就经验教你字符处理八字真言:确定编码,同类交互。
文章针对 Python 2.7 ,主要因为 3 对的编码已经有了很大的改善并且实际原理一样,更改一下操作命令即可。
了解完本文,你可以轻松解决文字处理,特殊平台( Windows?)下的编码,爬虫编码等问题。
本文分为如下几个部分:
如果想要了解我给出的使用习惯,可以直接跳到建议的使用习惯。
如果只想要解决相关问题可以直接跳到疑难问题解答。
希望本文能够帮到你。
为了理解方便,这里不谈理论只做类比,具体想要进一步了解各种编码的理论的搜狗一下好了。
首先说一下我们为什么会碰到各式各样的编码问题:
再说一下编码是什么, Python 的编码看似复杂,实际上可以看做只有两类编码: Unicode ,二进制
\u0000
这样的\x00\x00
这样的,平常看到的utf-8
,cp936
都是二进制编码10001100
原样就可以存储,而 Unicode 是抽象的,不能这样存#coding=utf8 # Unicode 编码演示 print('Unicode:') print(repr(u'Unicode 编码'))` # 二进制编码演示 print(u'二进制编码:') print(repr('Unicode 编码'))` # 只是看个样子,代码不必去深究
再说怎么做,就是只有同种编码之间才可以操作
就把一串数据比为烤鸭,我们作为人和鸭子不同种看待烤鸭的态度完全不一样。 我们看到的是晚上的配菜,鸭子看到的是自己二舅。 那么我在逛烤鸭店的时候用错编码就会报错。 因为我在烤鸭店看到了满世界的二舅。
utf-8
,unicode
,ucs-bom
最后说一下 Python 的环境
# 告知的命令就是下面这一行,删掉就会报错 #coding=utf8 print(u'测试编码')
拿到各种编码的内容自然是不用说,那么如果我们想要自己构造怎么做呢,看下面:
#coding=utf8 # 字符串前面加 u 会默认构造出 Unicode 的字符串 unicodeString = u'Unicode 字符串' # 字符串前面什么都不加会构造出默认编码(首行限定了现在的 utf8 )的字符串 utf8String = 'Utf-8 字符串' # 当然,没有首行,默认的编码是 Ascii
那么他们之间怎么转换呢,同样很简单:
# 接上一段程序 # Unicode 转化为二进制编码中的一种: utf8 unicodeString.encode('utf8') # 二进制编码根据自己的编码种类转化为 Unicode utf8String.decode('utf8') # 如果二进制编码中混进了奇怪的东西可以根据需求用特殊的 decode 策略 print(repr('u8 字\x00 符串'.decode('utf8', 'replace')))
那么怎么样会出现问题呢:
# 接上一段程序 # 如果我们把他们转化成同样的编码方式就可以操作(例如相加) print(repr(unicodeString + utf8String.decode('utf8'))) print(repr(unicodeString.encode('utf8') + utf8String)) # 但如果不转化,当然就会出现满世界的烤鸭二舅啦 unicodeString + utf8String # 所以另一方面也发现,编码转换是需要我们告诉程序怎么做的 # 所有`decode`操作都会生成 Unicode 编码,这是为了方便我之前说的大量接受 Unicode 的内部命令
所以我们需要确定程序使用的编码,这是我们需要告诉程序的东西
#coding=utf8 # 这里拿写入文件举例 # 一般使用 Unicode with open('Unicode.txt', 'w') as f: f.write(u'Unicode 测试') # 或者使用接收二进制编码的命令 with open('Utf8.txt', 'wb') as f: f.write('Utf8 测试') # 你可以反过来做个测试,自然会报错 # 二进制的命令方便了在不知道怎么解码的情况下也能进行操作(写入文件)
相信到这里我已经把我对于编码的理解讲完了。
我们为什么会碰到各式各样的编码问题:
所以这里再重申一下八字真言:确定编码,同类交互
这里给出我的使用习惯:
记得在开始整个程序之前确定内部的编码,否则编码一团糟会产生很多不必要的 bug 。
不要迷信内部 Unicode ,例如 Evernote 开发就应该根据第三方包使用的 Utf8 确定内部编码。
说了要确定编码,那么拿到一串二进制要怎么确定编码呢?
最简单的方法是chardet
:(需要安装)
python -m pip install chardet
使用非常简单:
#coding=utf8 from chardet import detect print(detect('这是一串 utf8 的测试字符')) # 结果:`{'confidence': 0.99, 'encoding': 'utf-8'}`
另外例如抓取网站,那么头文件中很有可能有提示如何解码,记得不要忘记了。
很可能因为字符串中参杂了奇怪的东西,导致即使编码种类正确,依旧无法解码。
我知道我之前讲过了,但可能有人直接跳疑难问题解答嘛。
这里可以使用decode
的第二个参数:
#coding=utf8 # 字符串中混进了\x00 rubbishUtf8String = 'Utf-8 字\x00 符串' print(repr(rubbishUtf8String.decode('utf8', 'replace'))) print(repr(rubbishUtf8String.decode('utf8', 'ignore')))
很多人都说 Windows 是个坑,即使在 Python 3 下面也一样。
因为中文文件名出来都是乱码。
这里使用一个取巧的方法:平台编码再特殊,起码命令行读取和创建一个文件夹不会出乱码吧。
import sys, os for folder in os.walk('.').next()[1]: print(folder.decode(sys.stdin.encoding))
同样的输入输出也可以这样做优化:
imort sys def sys_print(msg): print(msg.encode(sys.stdin.encoding)) def sys_input(msg): return raw_input(msg.encode(sys.stdin.encoding)).decode(sys.stdin.encoding)
如果抓下来一个内容不知道怎么解码,但还是想要写入文件怎么办
写入文件的时候制定用二进制命令即可:
#coding=utf8 import urllib with open('Utf8.txt', 'wb') as f: f.write('Utf8 测试') # 比如抓了个网页,不知道编码也可以写入文件进行一系列操作 cOntent= urllib.urlopen('http://www.baidu.com').read() with open('baidu.txt', 'wb') as f: f.write(content)
Unicode 存成六个 Ascii 字符怎么办?其实也可以decode
#coding=utf8 # 这是普通的 Unicode s = u'测' for i in s: print(i) print(repr(s)) # 这是裸 Unicode ,实际存成了六个 Ascii s = repr(s)[2:-1] for i in s: print(i) print(repr(s)) # 转化其实也很简单 s = s.decode('unicode-escape') for i in s: print(i) print(repr(s))
希望读完这篇文章能对你有帮助,有什么不足之处万望指正(鞠躬)。
有什么想法或者想要关注我的更新,欢迎来Github上Star或者Fork我的项目。
160623
LittleCoder
EOF
![]() | 1 louzhumuyou 2016-06-23 10:54:22 +08:00 赞一个,虽然看的懵懵懂懂的 |
![]() | 2 magicdawn 2016-06-23 10:56:16 +08:00 pyenv install 3.5.1 # 哈哈 |
![]() | 3 est 2016-06-23 11:05:04 +08:00 ![]() 一本正经的胡说八道 |
![]() | 4 NxnXgpuPSfsIT OP @est 怎么说? |
![]() | 5 ryd994 2016-06-23 11:07:54 +08:00 via Android ![]() 没别的事就全用 Unicode ……… |
![]() | 6 NxnXgpuPSfsIT OP @magicdawn 很多时候碰到编码问题的第一建议都是,去用 3 ,哈哈 |
![]() | 7 NxnXgpuPSfsIT OP @ryd994 对的,非常赞同。不过出于严谨考虑,用第三方包之前还是可以考虑一下第三方包的编码,我写命令行印象笔记的时候中期才改 Utf8 编码方式,非常痛苦。 |
![]() | 8 practicer 2016-06-23 11:16:43 +08:00 在自学 python 五个月后突然明白了编码问题, 抄代码抄得竟然想通了 =。= |
![]() | 9 NxnXgpuPSfsIT OP @practicer 我也这样,也是悲伤 |
![]() | 10 xiandao7997 2016-06-23 12:23:19 +08:00 via Android @practicer 想通了那有分享吗? |
![]() | 11 practicer 2016-06-23 12:41:31 +08:00 @xiandao7997 没有很系统的了解,只有当遇到具体编码 error 时知道怎么调,我用 py2 入的门。 例如当返回的字符有编码错误,我先 type(x)查一下该结果是 string 还是 Unicode ,如果是 U ,直接 encode('GBK')或 encode('UTF-8'),此时如果仍然出错,看具体 error ,大多数情况那是因为结果中还存在连'GBK'或'UTF-8'编码都不认识的字符,这个时候用 encode('GBK', 'ignore'),即可过滤那部分不认识的字符, error 就消失了。 当 type(x)为 string 时,那么需要先解码为 Unicode 。选 decode('GBK')还是 decode('UTF-8'),或其他编码?需要先了解传入解释器的源字符串的编码格式,如果字符串来自于所爬取的是 utf-8 网页,则选 decode('UTF-8'),如果是 GBK ,则 decode('GBK')。 decode 后字符串转成了 unicode ,面对 Unicode ,和前面处理 Unicode 相同,根据自己的需求 encode 即可。 |
![]() | 12 imn1 2016-06-23 14:01:15 +08:00 windows 还需要讲 cmd 上的输出, py3 输入不难解决 此外,不论什么语言,数据库的编码也是要重点讲的,很多人存入再读出后就傻眼了 |
![]() | 13 imn1 2016-06-23 14:02:16 +08:00 另外,这文 5 分钟读不完 |
![]() | 14 mozartgho 2016-06-23 14:31:17 +08:00 写的很不错,感谢分享! |
![]() | 15 zhangbohun 2016-06-23 14:32:13 +08:00 |
16 killerv 2016-06-23 14:32:48 +08:00 我是全用 unicode |
17 nankingpython 2016-06-24 03:57:54 +08:00 t/287940#reply0 看了你的字。为什么这样不行呢 |
![]() | 18 dofine 2016-06-24 12:36:34 +08:00 via iPhone 本来感觉自己挺懂的…看完怎么更蒙蔽了…… |
![]() | 19 shamashii 2016-06-24 13:10:03 +08:00 然而一些 unicode-only 字符 cmd 上 print 还是无力,比如、。另外还和字体有关, cmd 下即使调成了 65001 也就支持两个 console 字体。幸好写入文件什么的没问题,虽然 print 乱码 |
![]() | 20 sxul07 2016-06-24 13:19:36 +08:00 [在烤鸭店看到了满世界的二舅] 这个比喻笑 cry |
21 dossec 2016-06-24 18:39:46 +08:00 这个不错,学习了。 |
![]() | 22 NxnXgpuPSfsIT OP @nankingpython 已回复 |
23 nankingpython 2016-06-24 23:05:24 +08:00 @NxnXgpuPSfsIT 太感谢了,解决了,虽然打不出中文好歹不报错了。打出全是 /xxx 之类的,我研究下 |