从零开始理解字符串编码 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Windsooon

从零开始理解字符串编码

  •  
  •   Windsooon 2018 年 4 月 19 日 3049 次点击
    这是一个创建于 2929 天前的主题,其中的信息可能已经有所发展或是发生改变。

    字符串编码可以说新手必碰壁,我以前看了很多文章还是感觉朦朦胧胧,所以这篇文章我从字符串存储开始介绍字符串编码的实现,希望可以帮到各位。一文理解字符串编码

    10 条回复    2018-05-12 12:02:24 +08:00
    jingniao
        1
    jingniao  
       2018 年 4 月 19 日
    其实弄清楚
    字节
    字符
    编码
    解码
    之间的关系,应该好理解好多。
    然后对于各个语言,对字符串,到底是默认 unicode 还是 bytes
    faemon
        2
    faemon  
       2018 年 4 月 19 日 via iPhone
    新手弄不清楚的话的确很懵,这篇文章挺好的
    sundev
        3
    sundev  
       2018 年 4 月 19 日
    现在基本都是 utf8 一统江湖了吧。
    但是有时候真出现乱码问题,真要解决必须得懂得这些知识,还要熟知各个环节(语言、服务、数据库、文件等)获取到的数据的编码是什么,对能力是很大的考验。
    mortonnex
        4
    mortonnex  
       2018 年 4 月 19 日
    牛!
    di94sh
        5
    di94sh  
       2018 年 4 月 19 日 via Android
    其实很简单的逻辑,字符组成字符集,比如 gbk unicode 等等字符集。然后现在世界上个语言字符收录最全的,应用最广的是 Unicode 字符集。然后实现一个字符集有很多种编码方式,utf-8 utf-16 等等 都是一些实现 unicode 字符集的编码方式 二进制 转到 Unicode

    然后在编码里 你要明白你写的语言的字面到底是二进制,还是 Unicode 比如 python

    python2 字符串字面上是二进制的 然后你如果转成 Unicode 的 python2 的默认转码方式是 assic 只支持英文,数字,标点,等字符。
    所以在处理非英语的时候会出错误,你在转吗的时候指定 utf-8 就好了。
    python3 字符串字面上是 Unicode 的 然后 python3 的默认编码方式是 utf-8,所以你转吗的时候可以使用默认编码

    最后 二进制 → Unicode 用 decode 解码
    Unicode → 二进制 用 encode 编码
    hxndg
        6
    hxndg  
       2018 年 4 月 19 日
    很不错的文章,提几点建议:
    1去掉那几张巨大而且不相关的图片。
    2加上不同语言中对于字符的默认处理和转换,比方说 python2.7, python3, ruby 等。
    3提及计算机大端小端和网络上的大端小端。
    Windsooon
        7
    Windsooon  
    OP
       2018 年 4 月 19 日
    @di94sh 说的没错,主要要理解 Unicode 是一种规范而不是具体的存储方式
    Windsooon
        8
    Windsooon  
    OP
       2018 年 4 月 19 日
    @hxndg 谢谢,第一点我会换小一点图片的,哈哈,第二点我会在常用问题增加的,第三点的话我不确定需不需要添加,我怕会让读者更加困惑。
    Sylv
        9
    Sylv  
       2018 年 5 月 12 日
    文章很不错。

    不过挑几个 Python 2 相关的错,或者说是表述不准确的地方:

    1--

    「 python2 是把字符串直接经过 utf-8 编码保存的」
    「出错的原因是因为 python2 当遇到 ASCII 表中没有的字符的时候,默认会把它们使用 utf-8 编码来存储」

    其实并不是这样的,Python 2 并没有默认用 utf-8 编码来保存字符串,实际上在终端下输入的字符串的编码是由终端的编码决定的,也就是说终端用的是什么编码,Python 接收到的字符串就是什么编码。你这句话最多只能适用于 Mac 和大多数 Linux 系统等,因为它们终端的默认编码是 utf-8,所以在终端下输入 "你好",Python 接收到的是 utf-8 编码的 '\xe4\xbd\xa0\xe5\xa5\xbd'。但是在 Windows 下就不是这样了:
    >>> '你好'
    '\xc4\xe3\xba\xc3'

    结果并不是 utf-8 编码的 '\xe4\xbd\xa0\xe5\xa5\xbd'。这是因为中文 Windows 系统 cmd 的默认编码是 cp936 (GBK),而不是 utf-8。可以通过 sys.stdin.encoding 来查看这个编码:
    >>> import sys
    >>> sys.stdin.encoding
    'cp936'

    所以在这种情况下输入 "你好",Python 接收到的二进制数据会是 '\xc4\xe3\xba\xc3',也就是 "你好" 的 cp936 编码:
    >>> u'你好'.encode('cp936')
    '\xc4\xe3\xba\xc3'

    这个道理和 Python 读取文件内字符串是一样的,文件是什么编码,Python 读取到的字符串就是什么编码。Python 2 并没有默认使用 utf-8 编码来保存或读取字符串,而是由输入端(文件、终端等)的编码决定了 Python 2 获取到的字符串的编码。

    2--


    >>> '你好'.encode('')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

    出错的原因是因为 python2 当遇到 ASCII 表中没有的字符的时候,默认会把它们使用 utf-8 编码来存储,但是 gbk 编码表无法对 utf-8 编码进行解码,这句话你可能需要读完文章才能理解。


    这个例子里出错的原因并不是 gbk 编码表无法对 utf-8 编码进行解码。注意看这里报的错是 UnicodeDecodeError: 'ascii' codec can't decode,说的是 Python 无法用 ascii 编码来解码 '你好',和 gbk () 编码没关系。而且 encode 是执行编码操作不是解码操作,你是在用 对字符串进行编码,不是你文中说的「 gbk 编码表……进行解码」。

    这里出错的真正原因是:你在 Python 2 下对 str 类型的字符串进行了 encode 操作,encode 操作只对 unicode 类型字符串才有意义( Python 3 里甚至不允许对非 unicode 字符串 (bytes) 进行 encode 操作,也不允许对 unicode 字符串 (str) 进行 decode 操作),所以 Python 2 在这里会隐式地尝试将 str 类型的 '你好' decode 为 unicode 类型字符串后再进行你要的 encode,这个隐式的 decode 操作使用的编码是 Python 2 的默认编码 ascii,Python 的默认编码可以通过以下命令查看:
    >>> import sys
    >>> sys.getdefaultencoding()
    'ascii'

    所以执行 '你好'.encode('') 时相当于执行了:
    '你好'.decode('ascii').encode('')

    ascii 无法解码中文,所以报了 UnicodeDecodeError 错,此时还没有真正执行 encode(''),所以例子和你的解释并不适用。

    正确的例子应该是:
    >>> '\xe4\xbd\xa0\xe5\xa5\xbd'.decode('gbk')
    u'\u6d63\u72b2\u30bd'
    >>> u'\u6d63\u72b2\u30bd' == u'你好'
    False

    不用 '你好'.decode('gbk') 来举例是因为正如上文 1 所说,这段代码在中文 Windows 系统下是没有编码错误的:
    >>> '你好'.decode('gbk') == u'你好'
    True

    而 '\xe4\xbd\xa0\xe5\xa5\xbd'.decode('gbk') 这个例子你会发现 Python 2 是能顺利运行没有报错的,并不像你说的「 gbk 编码表无法对 utf-8 编码进行解码」那样出现 UnicodeDecodeError 错误。甚至这个结果是能直接 print 出来的:
    >>> print('\xe4\xbd\xa0\xe5\xa5\xbd'.decode('gbk'))
    浣ソ

    这是因为 '你好' 的 utf-8 编码和 '浣ソ' 的 gbk 编码是重合的,都是 '\xe4\xbd\xa0\xe5\xa5\xbd':
    >>> u'你好'.encode('utf-8')
    '\xe4\xbd\xa0\xe5\xa5\xbd'
    >>> u'浣ソ'.encode('gbk')
    '\xe4\xbd\xa0\xe5\xa5\xbd'

    所以 gbk 编码表其实是可以对部分 utf-8 编码的字符串进行解码的,只是解码的结果不是我们想要的,也就是出现了乱码问题。

    3--


    另外常见编码错误就是使用错误的编码保存字符串,例如使用 ASCII 表保存”你好”,因为 ASCII 表里面没有对应的字符,它不知道如何保存。

    >>> '你好'.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)


    这个例子应该是在 Python 3 下执行的结果,'你好' 得是 unicode 类型字符串对其 encode('ascii') 才会报 UnicodeEncodeError 错误,否则在 Python 2 下应该如 2 的例子一样报的是 UnicodeDecodeError 错误:
    >>> '你好'.encode('ascii')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

    也就是相当于执行了:
    '你好'.decode('ascii').encode('ascii')

    正确的例子应该是:
    >>> u'你好'.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)

    --

    我以前也写过篇和 Python 2 中文 Unicode 编码问题有关的文章,供你参考:
    t/163786
    Windsooon
        10
    Windsooon  
    OP
        1
    @Sylv 你说的很有道理,我从你的评论里面也学到很多,原本的文章确实有错误和不严谨的地方。我会在两天内根据你的建议修改好的,你的文章写得很好。谢谢你的回复。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2384 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 42ms UTC 16:09 PVG 00:09 LAX 09:09 JFK 12:09
    Do have faith in what you're doing.
    ubao msn 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