五分钟战胜 Python 字符编码 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
NxnXgpuPSfsIT
V2EX    Python

五分钟战胜 Python 字符编码

  •  
  •   NxnXgpuPSfsIT
    littlecodersh 2016-06-23 10:44:08 +08:00 6375 次点击
    这是一个创建于 3399 天前的主题,其中的信息可能已经有所发展或是发生改变。

    五分钟战胜 Python 字符编码

    对于很多接触 Python 的人而言,字符的处理和语言整体的温顺可靠相比显得格外桀骜不驯难以驾驭。

    本文不谈复杂的理论,就经验教你字符处理八字真言:确定编码,同类交互。

    文章针对 Python 2.7 ,主要因为 3 对的编码已经有了很大的改善并且实际原理一样,更改一下操作命令即可。

    了解完本文,你可以轻松解决文字处理,特殊平台( Windows?)下的编码,爬虫编码等问题。

    阅读建议

    本文分为如下几个部分:

    • 原理
    • 具体操作
    • 建议的使用习惯
    • 疑难问题解答

    如果想要了解我给出的使用习惯,可以直接跳到建议的使用习惯。

    如果只想要解决相关问题可以直接跳到疑难问题解答。

    希望本文能够帮到你。

    原理

    为了理解方便,这里不谈理论只做类比,具体想要进一步了解各种编码的理论的搜狗一下好了。

    首先说一下我们为什么会碰到各式各样的编码问题:

    • 因为我们没有统一编码
    • 因为我们没有用对命令(传对数据)

    再说一下编码是什么, Python 的编码看似复杂,实际上可以看做只有两类编码: Unicode ,二进制

    • 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 的环境

    • 本身代码是用 Ascii 解码的,文件里有 Ascii 无法解码的内容的话要告知 Python 怎么解码
    • 内部大量命令都是默认接受 Unicode
    # 告知的命令就是下面这一行,删掉就会报错 #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 的内部命令 

    所以我们需要确定程序使用的编码,这是我们需要告诉程序的东西

    • 一方面在操作字符串的时候确定是同种编码
    • 另一方面在使用非自己写的命令时,一般使用 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 测试') # 你可以反过来做个测试,自然会报错 # 二进制的命令方便了在不知道怎么解码的情况下也能进行操作(写入文件) 

    我建议的使用习惯

    相信到这里我已经把我对于编码的理解讲完了。

    我们为什么会碰到各式各样的编码问题:

    • 因为我们没有统一编码
    • 因为我们没有用对命令(传对数据)

    所以这里再重申一下八字真言:确定编码,同类交互

    • 碰到问题,问一下自己,我现在是哪种编码
    • 同一种编码才能交互,那我应该是哪种编码

    这里给出我的使用习惯:

    • 确定一种内部编码
    • 内部编码的选择优先级如下:程序必须使用的编码、第三方包使用的编码、你喜欢的编码、 Unicode
    • 在输出时再更改到特定的编码

    记得在开始整个程序之前确定内部的编码,否则编码一团糟会产生很多不必要的 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 字符

    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)) 

    结束语

    希望读完这篇文章能对你有帮助,有什么不足之处万望指正(鞠躬)。

    有什么想法或者想要关注我的更新,欢迎来GithubStar或者Fork我的项目。

    160623

    LittleCoder

    EOF

    23 条回复    2016-06-24 23:05:24 +08:00
    louzhumuyou
        1
    louzhumuyou  
       2016-06-23 10:54:22 +08:00
    赞一个,虽然看的懵懵懂懂的
    magicdawn
        2
    magicdawn  
       2016-06-23 10:56:16 +08:00
    pyenv install 3.5.1 # 哈哈
    est
        3
    est  
       2016-06-23 11:05:04 +08:00   3
    一本正经的胡说八道
    NxnXgpuPSfsIT
        4
    NxnXgpuPSfsIT  
    OP
       2016-06-23 11:07:12 +08:00
    @est 怎么说?
    ryd994
        5
    ryd994  
       2016-06-23 11:07:54 +08:00 via Android   1
    没别的事就全用 Unicode ………
    NxnXgpuPSfsIT
        6
    NxnXgpuPSfsIT  
    OP
       2016-06-23 11:08:11 +08:00
    @magicdawn 很多时候碰到编码问题的第一建议都是,去用 3 ,哈哈
    NxnXgpuPSfsIT
        7
    NxnXgpuPSfsIT  
    OP
       2016-06-23 11:11:36 +08:00
    @ryd994 对的,非常赞同。不过出于严谨考虑,用第三方包之前还是可以考虑一下第三方包的编码,我写命令行印象笔记的时候中期才改 Utf8 编码方式,非常痛苦。
    practicer
        8
    practicer  
       2016-06-23 11:16:43 +08:00
    在自学 python 五个月后突然明白了编码问题,


    抄代码抄得竟然想通了 =。=
    NxnXgpuPSfsIT
        9
    NxnXgpuPSfsIT  
    OP
       2016-06-23 11:19:40 +08:00
    @practicer 我也这样,也是悲伤
    xiandao7997
        10
    xiandao7997  
       2016-06-23 12:23:19 +08:00 via Android
    @practicer 想通了那有分享吗?
    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 即可。
    imn1
        12
    imn1  
       2016-06-23 14:01:15 +08:00
    windows 还需要讲 cmd 上的输出, py3 输入不难解决
    此外,不论什么语言,数据库的编码也是要重点讲的,很多人存入再读出后就傻眼了
    imn1
        13
    imn1  
       2016-06-23 14:02:16 +08:00
    另外,这文 5 分钟读不完
    mozartgho
        14
    mozartgho  
       2016-06-23 14:31:17 +08:00
    写的很不错,感谢分享!
    killerv
        16
    killerv  
       2016-06-23 14:32:48 +08:00
    我是全用 unicode
    nankingpython
        17
    nankingpython  
       2016-06-24 03:57:54 +08:00
    t/287940#reply0 看了你的字。为什么这样不行呢
    dofine
        18
    dofine  
       2016-06-24 12:36:34 +08:00 via iPhone
    本来感觉自己挺懂的…看完怎么更蒙蔽了……
    shamashii
        19
    shamashii  
       2016-06-24 13:10:03 +08:00
    然而一些 unicode-only 字符 cmd 上 print 还是无力,比如、。另外还和字体有关, cmd 下即使调成了 65001 也就支持两个 console 字体。幸好写入文件什么的没问题,虽然 print 乱码
    sxul07
        20
    sxul07  
       2016-06-24 13:19:36 +08:00
    [在烤鸭店看到了满世界的二舅]
    这个比喻笑 cry
    dossec
        21
    dossec  
       2016-06-24 18:39:46 +08:00
    这个不错,学习了。
    NxnXgpuPSfsIT
        22
    NxnXgpuPSfsIT  
    OP
       2016-06-24 19:13:15 +08:00
    @nankingpython 已回复
    nankingpython
        23
    nankingpython  
       2016-06-24 23:05:24 +08:00
    @NxnXgpuPSfsIT 太感谢了,解决了,虽然打不出中文好歹不报错了。打出全是 /xxx 之类的,我研究下
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1071 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 23:03 PVG 07:03 LAX 16:03 JFK 19:03
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86