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
lzjun
V2EX    Python

Python 编码为什么那么蛋疼?

  •  2
     
  •   lzjun
    lzjun567 2017-03-20 14:15:29 +08:00 8350 次点击
    这是一个创建于 3128 天前的主题,其中的信息可能已经有所发展或是发生改变。

    据说,每个做 Python 开发的都被字符编码的问题搞晕过,最常见的错误就是 UnicodeEncodeError 、 UnicodeDecodeError ,你好像知道怎么解决,遗憾的是,错误又出现在其它地方,问题总是重蹈覆辙, str 到 unicode 之间的转换用 decode 还是 encode 方法还特不好记,老是混淆,问题究竟出在哪里?

    为了弄清楚这个问题,我决定从 python 字符串的构成以及字符编码的细节上进行深入浅出的分析

    字节与字符

    计算机存储的一切数据,文本字符、图片、视频、音频、软件都是由一串 01 的字节序列构成的,一个字节等于 8 个比特位。

    而字符就是一个符号,比如一个汉字、一个英文字母、一个数字、一个标点都可以称为一个字符。

    字节方便存储和网络传输,而字符用于显示,方便阅读。例如字符 "p" 存储到硬盘是一串二进制数据 01110000,占用一个字节的长度

    编码与解码

    我们用编辑器打开的文本,看到的一个个字符,最终保存在磁盘的时候都是以二进制字节序列形式存起来的。那么从字符到字节的转换过程就叫做编码( encode ),反过来叫做解码( decode ),两者是一个可逆的过程。编码是为了存储传输,解码是为了方便显示阅读。

    例如字符 "p" 经过编码处理保存到硬盘是一串二进制字节序列 01110000 ,占用一个字节的长度。字符 "禅" 有可能是以 "11100111 10100110 10000101" 占用 3 个字节的长度存储,为什么说是有可能呢?这个放到后面再说。

    Python 的编码为什么那么蛋疼?当然,这不能怪开发者。

    这是因为 Python2 使用 ASCII 字符编码作为默认编码方式,而 ASCII 不能处理中文,那么为什么不用 UTf-8 呢?因为 Guido 老爹为 Python 编写第一行代码是在 1989 年的冬天, 1991 年 2 月正式开源发布了第一个版本,而 Unicode 是 1991 年 10 月发布的,也就是说 Python 这门语言创立的时候 UTF-8 还没诞生,这是其一。

    Python 把字符串的类型还搞成两种, unicode 和 str ,以至于把开发者都弄糊涂了,这是其二。 python3 彻底把 字符串重新改造了,只保留一种类型,这是后话,以后再说。

    str 与 unicode

    Python2 把字符串分为 unicode 和 str 两种类型。本质上 str 是一串二进制字节序列,下面的示例代码可以看出 str 类型的 "禅" 打印出来是十六进制的 \xec\xf8 ,对应的二进制字节序列就是 '11101100 11111000'。

    >>> s = '禅' >>> s '\xec\xf8' >>> type(s) <type 'str'> 

    而 unicode 类型的 u"禅" 对应的 unicode 符号是 u'\u7985'

    >>> u = u"禅" >>> u u'\u7985' >>> type(u) <type 'unicode'> 

    我们要把 unicode 符号保存到文件或者传输到网络就需要经过编码处理转换成 str 类型,于是 python 提供了 encode 方法,从 unicode 转换到 str ,反之亦然。

    python2-str

    encode

    >>> u = u"禅" >>> u u'\u7985' >>> u.encode("utf-8") '\xe7\xa6\x85' 

    decode

    >>> s = "禅" >>> s.decode("utf-8") u'\u7985' >>> 

    不少初学者怎么也记不住 str 与 unicode 之间的转换用 encode 还是 decode ,如果你记住了 str 本质上其实是一串二进制数据,而 unicode 是字符(符号),编码( encode )就是把字符(符号)转换为 二进制数据的过程,因此 unicode 到 str 的转换要用 encode 方法,反过来就是用 decode 方法。

    encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string".

    清楚了 str 与 unicode 之间的转换关系之后,我们来看看什么时候会出现 UnicodeEncodeError 、 UnicodeDecodeError 错误。

    UnicodeEncodeError

    UnicodeEncodeError 发生在 unicode 字符串转换成 str 字节序列的时候,来看一个例子,把一串 unicode 字符串保存到文件

    # -*- coding:utf-8 -*- def main(): name = u'Python 之禅' f = open("output.txt", "w") f.write(name) 

    错误日志

    UnicodeEncodeError: 'ascii' codec can't encode characters in position 6-7: ordinal not in range(128)

    为什么会出现 UnicodeEncodeError ?

    因为调用 write 方法时, Python 会先判断字符串是什么类型,如果是 str ,就直接写入文件,不需要编码,因为 str 类型的字符串本身就是一串二进制的字节序列了。

    如果字符串是 unicode 类型,那么它会先调用 encode 方法把 unicode 字符串转换成二进制形式的 str 类型,才保存到文件,而 encode 方法会使用 python 默认的 ascii 码来编码

    相当于:

    >>> u"Python 之禅".encode("ascii") 

    但是,我们知道 ASCII 字符集中只包含了 128 个拉丁字母,不包括中文字符,因此 出现了 'ascii' codec can't encode characters 的错误。要正确地使用 encode ,就必须指定一个包含了中文字符的字符集,比如: UTF-8 、 GBK 。

    >>> u"Python 之禅".encode("utf-8") 'Python\xe4\xb9\x8b\xe7\xa6\x85' >>> u"Python 之禅".encode("gbk") 'Python\xd6\xae\xec\xf8' 

    所以要把 unicode 字符串正确地写入文件,就应该预先把字符串进行 UTF-8 或 GBK 编码转换。

    def main(): name = u'Python 之禅' name = name.encode('utf-8') with open("output.txt", "w") as f: f.write(name) 

    当然,把 unicode 字符串正确地写入文件不止一种方式,但原理是一样的,这里不再介绍,把字符串写入数据库,传输到网络都是同样的原理

    UnicodeDecodeError

    UnicodeDecodeError 发生在 str 类型的字节序列解码成 unicode 类型的字符串时

    >>> a = u"禅" >>> a u'\u7985' >>> b = a.encode("utf-8") >>> b '\xe7\xa6\x85' >>> b.decode("gbk") Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'gbk' codec can't decode byte 0x85 in position 2: incomplete multibyte sequence 

    把一个经过 UTF-8 编码后生成的字节序列 '\xe7\xa6\85' 再用 GBK 解码转换成 unicode 字符串时,出现 UnicodeDecodeError ,因为 (对于中文字符) GBK 编码只占用两个字节,而 UTF-8 占用 3 个字节,用 GBK 转换时,还多出一个字节,因此它没法解析。避免 UnicodeDecodeError 的关键是保持 编码和解码时用的编码类型一致。

    这也回答了文章开头说的字符 "禅",保存到文件中有可能占 3 个字节,有可能占 2 个字节,具体处决于 encode 的时候指定的编码格式是什么。

    再举一个 UnicodeDecodeError 的例子

    >>> x = u"Python" >>> y = "之禅" >>> x + y 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) >>> 

    str 与 unicode 字符串 执行 + 操作是, Python 会把 str 类型的字节序列隐式地转换成(解码)成 和 x 一样的 unicode 类型,但 Python 是使用默认的 ascii 编码来转换的,而 ASCII 中不包含中文,所以报错了。

    >>> y.decode('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) 

    正确地方式应该是显示地把 y 用 UTF-8 或者 GBK 进行解码。

    >>> x = u"Python" >>> y = "之禅" >>> y = y.decode("utf-8") >>> x + y u'Python\u4e4b\u7985' 

    以上内容都是基于 Python2 来讲的,关于 Python3 的字符和编码将会另开一篇文章来写,保持关注。

    原文地址:https://mp.weixin.qq.com/s/LQrPmp2HMlw5C7izJIUHNQ
    作者:liuzhijun

    66 条回复    2017-03-23 13:30:51 +08:00
    Ixizi
        1
    Ixizi  
       2017-03-20 14:58:54 +08:00
    nice
    hjc4869
        2
    hjc4869  
       2017-03-20 15:08:30 +08:00 via Android   6
    看标题以为是喷 python 2 ,进来才发现是编码科普文
    fy
        3
    fy  
       2017-03-20 15:08:54 +08:00   4
    为什么蛋疼? -> 请用 Python3 ,下一题。
    glasslion
        4
    glasslion  
       2017-03-20 15:13:07 +08:00   4
    编码一直是就是一件很蛋疼的事, 其他语言看上去不那么蛋疼,无非是:
    1. 不检查编码 /解码是否会报错。 手持两把锟斤拷, 口中直呼烫烫烫 就是这么来的。
    2. 只支持 utf-8 这一种编码
    lzjun
        5
    lzjun  
    OP
       2017-03-20 15:22:25 +08:00
    @hjc4869

    Python 的编码为什么那么蛋疼?当然,这不能怪开发者。

    这是因为 Python2 使用 ASCII 字符编码作为默认编码方式,而 ASCII 不能处理中文,那么为什么不用 UTf-8 呢?因为 Guido 老爹为 Python 编写第一行代码是在 1989 年的冬天, 1991 年 2 月正式开源发布了第一个版本,而 Unicode 是 1991 年 10 月发布的,也就是说 Python 这门语言创立的时候 UTF-8 还没诞生,这是其一。

    Python 把字符串的类型还搞成两种, unicode 和 str ,以至于把开发者都弄糊涂了,这是其二
    call43848
        6
    call43848  
       2017-03-20 15:28:22 +08:00   1
    神曰:“用 3 ”。
    gimp
        7
    gimp  
       2017-03-20 15:29:06 +08:00
    @glasslion

    “手持两把锟斤拷, 口中直呼烫烫烫” 笑了
    Gsyc1
        8
    Gsyc1  
       2017-03-20 15:57:04 +08:00
    用 Python 3 ,字符串默认是 unicode 的
    gouchaoer
        9
    gouchaoer  
       2017-03-20 15:59:50 +08:00   1
    我到现在也没搞懂编码,我为什么要搞清楚编码呢?我用 java 用 php 就没操过心
    lzjun
        10
    lzjun  
    OP
       2017-03-20 16:02:12 +08:00
    @gouchaoer 黑的漂亮,哈哈
    janxin
        11
    janxin  
       2017-03-20 16:08:38 +08:00
    用 python3
    aploium
        12
    aploium  
       2017-03-20 16:21:34 +08:00
    from __future__ import unicode_literals
    sagaxu
        13
    sagaxu  
       2017-03-20 16:22:25 +08:00
    @gouchaoer
    那是因为 php 只有 str 没有 unicode(php6 有,但夭折了),而 Java 只有 unicode 没有 str(str 用 byte[])。
    qingshi
        14
    qingshi  
       2017-03-20 16:28:44 +08:00
    @Gsyc1 默认是 utf-8 吧
    helloSwift
        15
    helloSwift  
       2017-03-20 16:29:33 +08:00 via iPhone
    为什么你写了这么多,不去看看 Python3 呢(_`)
    keisuu
        16
    keisuu  
       2017-03-20 16:57:01 +08:00
    @helloSwift 楼主分析的是 python2 。

    看完全文,我算是了解为什么老是报编码错误的原因了。

    ps: python3 一样的有类似的错误吧。
    lzjun
        17
    lzjun  
    OP
       2017-03-20 17:09:52 +08:00
    @helloSwift 啥意思? Python3 好呀
    everhythm
        18
    everhythm  
       2017-03-20 17:47:07 +08:00
    python 3 还是有编码错呀

    比如你用 vim 打开 1 个文件,生成个 .swp 文件

    如果用 python 读取到这个文件,就报错
    hjc4869
        19
    hjc4869  
       2017-03-20 17:50:11 +08:00 via Android
    @lzjun 其实用 byte[]来表示字符串也算是 UNIX 和 C 的遗毒了。现代语言在设计的时候基本都是以 code point 为单位,虽然 Java 等语言被 UCS-2 坑了…
    thekll
        20
    thekll  
       2017-03-20 18:36:59 +08:00 via iPhone
    很奇怪的 coding 方式。
    是说内存中保存 unicode code point , I/O 时再编码 /解码吗?
    weyou
        21
    weyou  
       2017-03-20 18:40:05 +08:00
    @everhythm .swp 是 binary 文件,你要用 rb 模式去读, 跟编码没有关系好不好?
    Xrong
        22
    Xrong  
       2017-03-20 18:41:55 +08:00   1
    关键是大伙都心想着先搞定功能吧,没空去了解一些编码的知识。
    lzjun
        23
    lzjun  
    OP
       2017-03-20 19:22:24 +08:00 via iPhone
    @Xrong 不去了解基础知识,遇到问题有时 neng 卡半天,关键是下次还是不知道问题根源
    lzjun
        24
    lzjun  
    OP
       2017-03-20 19:23:48 +08:00 via iPhone
    @hjc4869 为什么 UCS-2 算坑呢?
    hjc4869
        25
    hjc4869  
       2017-03-20 19:50:32 +08:00
    @lzjun UCS-2 是按 16bit 为一个 code point 的,那个时期的新软件(如 Java, Windows NT, Mac OS X, Qt 等)因为定长编码的优势和支持 Unicode 的需求,几乎都用了它。但是后来由于 16bit 不能满足 Unicode 的新标准,于是不得不又改为变长编码( UTF-16 )。
    Java/C#里的 Character/char 最早是定义成 16bit 的 code point ,可以取 index 获得对应位置的 code point ,但现在不行了,比如取 emoji 就会取到半个字。
    21grams
        26
    21grams  
       2017-03-20 20:01:28 +08:00 via Android
    Python 2 是 2000 年发布的,在编码上不与时俱进还搞成这样是不可原谅的
    kikyous
        27
    kikyous  
       2017-03-20 20:41:31 +08:00 via Android
    收藏
    hjc4869
      nbsp; 28
    hjc4869  
       2017-03-20 20:42:27 +08:00
    @21grams Python 2 是兼容 Python 1 的吧
    21grams
        29
    21grams  
       2017-03-20 20:49:02 +08:00
    @hjc4869 #28 兼容也不会有什么困难吧
    sagaxu
        30
    sagaxu  
       2017-03-20 21:01:46 +08:00
    @hjc4869 要么变长不能 index ,要么定长浪费空间,总要折衷一下的
    hjc4869
        31
    hjc4869  
       2017-03-20 21:19:04 +08:00
    @sagaxu 然而 UTF-16 既浪费空间也不定长,唯一的优势就是处理简单,速度快
    sagaxu
        32
    sagaxu  
       2017-03-20 21:44:53 +08:00 via Android
    @hjc4869 utf16 就是 utf8 和 utf16 之间的折衷, utf16 可以容纳大部分常用字符, str 内部实现可以利用这一点,比如置一个标志位,没有超出 2 字节范围时,就直接定位到字节,超出时再遍历字节做定位。
    21grams
        33
    21grams  
       2017-03-20 21:56:25 +08:00
    @sagaxu #32 那不是还要遍历后才知道要不要设标志位,还是有额外开销。
    ledzep2
        34
    ledzep2  
       2017-03-20 22:11:31 +08:00
    y
    ledzep2
        35
    ledzep2  
       2017-03-20 22:13:53 +08:00
    手残了 不好意思。 其实 python2 编码解码蛮好用的, 比 c......
    sagaxu
        36
    sagaxu  
       2017-03-20 22:16:43 +08:00
    @21grams 就算不要标志位,从字节数组构造一个 unicode 字符串出来,也是需要遍历的,不然怎么知道是否符合 unicode 规范?主流高级语言,字符串都是 immutable 的,所以标志算出来之后不需要重算,并没有增加什么开销。

    事实上 python 就是这么实现的 unicode 字符串
    enum PyUnicode_Kind {
    /* String contains only wstr byte characters. This is only possible
    when the string was created with a legacy API and _PyUnicode_Ready()
    has not been called yet. */
    PyUnicode_WCHAR_KIND = 0,
    /* Return values of the PyUnicode_KIND() macro: */
    PyUnicode_1BYTE_KIND = 1,
    PyUnicode_2BYTE_KIND = 2,
    PyUnicode_4BYTE_KIND = 4
    };
    hjc4869
        37
    hjc4869  
       2017-03-20 22:16:58 +08:00
    @sagaxu 如果要遍历的话可以把每个 code point 的位置都找出来。这样不管有没有 non-BMP 字符都能 O(1)定位,直接取到 code point
    jy01264313
        38
    jy01264313  
       2017-03-20 22:18:26 +08:00
    我觉得还是想理解:字符集和编码的区别吧,别上来就混在一起讲,还是很晕
    sagaxu
        39
    sagaxu  
       2017-03-20 22:26:06 +08:00
    @hjc4869 记录每个字符的位置,同样需要额外的存储空间,而且实现会更复杂,所以一般用标志位加定长内部编码比较常见。
    Xrong
        40
    Xrong  
       2017-03-20 22:57:15 +08:00
    @lzjun 我都了解好几回了,忘了再查,查了又忘大概是这节奏。说真的 Python 的编码确实蛋疼的一逼。
    thekll
        41
    thekll  
       2017-03-21 00:48:30 +08:00
    其实所谓的 unicode 也就是用 utf-16 或 utf-32 编码(与 python 版本和编译设置有关),类似 java 虚拟机内部统一用 utf-16 表示字符串。
    PythonAnswer
        42
    PythonAnswer  
       2017-03-21 01:01:18 +08:00
    用 py3 就再也没搞过编码问题了。
    thekll
        43
    thekll  
       2017-03-21 01:03:13 +08:00
    https://docs.python.org/2/howto/unicode.html
    这个文档中关于 code points 的 utf-8 编码字节值的范围描述似乎也有些问题, code point≥128 时对应的 utf-8 字节值并不全都是 128 到 255 之间.
    Gsyc1
        44
    Gsyc1  
       2017-03-21 01:26:31 +08:00
    @qingshi 多谢指正
    inisun
        45
    inisun  
       2017-03-21 08:59:48 +08:00
    @gouchaoer PHP 的 UTF-8 也有 bom 的问题..
    abcbuzhiming
        46
    abcbuzhiming  
       2017-03-21 10:16:49 +08:00
    python2 的编码问题和 C 语言一样, python3 开始编码走的和 java 这类语言一样的路线,搞清这两点就明白了
    lzjun
        47
    lzjun  
    OP
       2017-03-21 10:24:08 +08:00
    @hjc4869 长知识了,感谢
    thekoc
        48
    thekoc  
       2017-03-21 10:29:50 +08:00
    LokiSharp
        49
    LokiSharp  
       2017-03-21 10:32:14 +08:00
    还有一个问题, Windows 下 python3 不能显示 utf-8
    vjnjc
        50
    vjnjc  
       2017-03-21 11:27:58 +08:00
    之前用 java 和另一个 phper 调试接口,我就记得 java 的 string 要去掉前面 2 个 byte ,后面的内容给 phper 才不会出错~~好像跟什么大头有关
    realpg
        51
    realpg  
    PRO
       2017-03-21 12:34:22 +08:00
    @ledzep2 #35
    觉得半残是最难用的
    全残的 C 自己处理 everything 所有都一样
    这个半残的 py2 最闹心 总犯错
    fy
        52
    fy  
       2017-03-21 13:47:14 +08:00
    @LokiSharp 这个是 cmd 的锅,没记错的话是只能显示本地编码支持的字符
    LokiSharp
        53
    LokiSharp  
       2017-03-21 13:51:53 +08:00
    @fy cmd 默认属性里不能选 utf-8 必须 chcp 65001 ,然后,每次运行都要敲这个命令
    keisuu
        54
    keisuu  
       2017-03-21 14:21:52 +08:00
    大赞,终于解决了内心的疑惑,已关注公众号
    kuntang
        55
    kuntang  
       2017-03-21 14:27:57 +08:00
    如果 py3 不正确地使用字符编码,一样可能导致 UnicodeXXXError ,关键还是要懂原理
    annielong
        56
    annielong  
       2017-03-21 16:24:06 +08:00
    理解是理解,但是爬起来照样会遇到编码问题,还是要手工分析转码
    zaishanfeng
        57
    zaishanfeng  
       2017-03-21 16:45:48 +08:00 via Android
    其实你只需要用上 django 的两个方法就行了, 也可以把这两个方法提出来。 自此以后我再也没遇到过字符编码问题。
    hosiet
        58
    hosiet  
       2017-03-21 17:41:06 +08:00 via Android
    我觉得万恶之首是隐式转换。某些开发者写代码时不注意,导致某些地方收到超出 ASCII 范围的数据就崩。另外各种库的参数究竟是 str 还是 unicode 如果不注意搞混也会出类似问题。
    voostar
        59
    voostar  
       2017-03-21 20:35:55 +08:00
    自从我用了 3 之后就解毒了
    ltux
        60
    ltux  
       2017-03-21 20:41:20 +08:00
    我觉得主要问题在于很多程序员是面向 stackoverflow 编程,遇到问题就满足于把代码改得“能跑”就行,不去深究问题的根儿在哪儿,所以今儿改好了明儿继续出问题。我不能说 Python2 的编码不蛋疼,但我觉得也不能说“不怪开发者”。
    imcocc
        61
    imcocc  
       2017-03-21 21:32:51 +08:00 via Android
    看到楼主说用浅显的文字解释,接下来看到这么长的文字,我觉得还是升 3 吧
    param
        62
    param  
       2017-03-21 22:39:22 +08:00
    每个做 Python 开发的都被字符编码的问题搞晕过?
    我没用过 python2 ,最多是看过,一直用 python3 ,似乎没有被字符编码坑过。
    ProjectSky
        63
    ProjectSky  
       2017-03-22 00:56:58 +08:00
    "手持两把锟斤拷, 口中直呼烫烫烫"
    莫名喜感。
    lzjun
        64
    lzjun  
    OP
       2017-03-22 09:30:16 +08:00
    @param 新系统用 py3 最好,还有很多老系统根本没法迁移,你问问豆瓣迁移到 3 是个多大的工程
    TanLian
        65
    TanLian  
       2017-03-22 14:24:36 +08:00
    确实是篇好文章!!!
    ap010gi2e
        66
    ap010gi2e  
       2017-03-23 13:30:51 +08:00
    看着挺容易理解的,挺好
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     824 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 21:02 PVG 05:02 LAX 14:02 JFK 17:02
    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