• python编码


    编码回顾

    先了解一下位,字节和kb是什么?

    编码的种类情况

    • ASCII 占1个字节,只支持英文
    • GB2312 占2个字节,支持6700+汉字
    • GBK GB2312的升级版,支持21000+汉字
    • Shift-JIS 日本字符
    • ks_c_5601-1987 韩国编码
    • TIS-620 泰国编码

    由于每个国家都有自己的字符,所以其对应关系也涵盖了自己国家的字符,但是以上编码都存在局限性,即:仅涵盖本国字符,无其他国家字符的对应关系。应运而生出现了万国码,他涵盖了全球所有的文字和二进制的对应关系,

    • Unicode 2-4字节 已经收录136690个字符,并还在一直不断扩张中...

    Unicode 起到了2个作用:

    1. 直接支持全球所有语言,每个国家都可以不用再使用自己之前的旧编码了,用unicode就可以了。(就跟英语是全球统一语言一样)
    2. unicode包含了跟全球所有国家编码的映射关系,为什么呢?后面再讲

    Unicode解决了字符和二进制的对应关系,但是使用unicode表示一个字符,太浪费空间。例如:利用unicode表示“Python”需要12个字节才能表示,比原来ASCII表示增加了1倍。

    由于计算机的内存比较大,并且字符串在内容中表示时也不会特别大,所以内容可以使用unicode来处理,但是存储和网络传输时一般数据都会非常多,那么增加1倍将是无法容忍的!!!

    为了解决存储和网络传输的问题,出现了Unicode Transformation Format,学术名UTF,即:对unicode中的进行转换,以便于在存储和网络传输时可以节省空间!

    • UTF-8: 使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多4个字节。英文占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个
    • UTF-16: 使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
    • UTF-32: 使用4个字节表示所有字符;

    总结:UTF 是为unicode编码 设计 的一种 在存储 和传输时节省空间的编码方案。

    字符在硬盘上的存储 

    无论以什么编码在内存里显示字符,存到硬盘上都是2进制。

    ascii编码(美国):
        l   0b1101100
        o   0b1101111
        v   0b1110110
        e   0b1100101
    GBK编码(中国):
        老   0b11000000 0b11001111
        男   0b11000100 0b11010000
        孩   0b10111010 0b10100010
     
    Shift_JIS编码(日本):
        私   0b10001110 0b10000100
        は   0b10000010 0b11001101
     
    ks_c_5601-1987编码(韩国):
        나   0b10110011 0b10101010
        는   0b10110100 0b11000010
         
    TIS-620编码(泰国):
        ฉัน  0b10101001 0b11010001 0b10111001

    要注意的是,存到硬盘上时是以何种编码存的,再从硬盘上读出来时,就必须以何种编码读,要不然就乱了

    编码的转换 

    虽然国际语言是英语 ,但大家在自己的国家依然说自已的语言,不过出了国, 你就得会英语
    编码也一样,虽然有了unicode and utf-8 , 但是由于历史问题,各个国家依然在大量使用自己的编码,比如中国的windows,默认编码依然是gbk,而不是utf-8

    基于此,如果中国的软件出口到美国,在美国人的电脑上就会显示乱码,因为他们没有gbk编码。
    若想让中国的软件可以正常的在 美国人的电脑上显示,只有以下2条路可走:

    1. 让美国人的电脑上都装上gbk编码
    2. 把你的软件编码以utf-8编码

    第1种方法几乎不可能实现,第2种方法比较简单。 但是也只能是针对新开发的软件。 如果你之前开发的软件就是以gbk编码的,上百万行代码可能已经写出去了,重新编码成utf-8格式也会费很大力气。

    so , 针对已经用gbk开发完毕的项目,以上2种方案都不能轻松的让项目在美国人电脑上正常显示,难道没有别的办法了么?
    有, 还记得我们讲unicode其中一个功能是其包含了跟全球所有国家编码的映射关系,意思就是,你写的是gbk的“路飞学城”,但是unicode能自动知道它在unicode中的“路飞学城”的编码是什么,如果这样的话,那是不是意味着,无论你以什么编码存储的数据 ,只要你的软件在把数据从硬盘读到内存里,转成unicode来显示,就可以了。
    由于所有的系统、编程语言都默认支持unicode,那你的gbk软件放到美国电脑 上,加载到内存里,变成了unicode,中文就可以正常展示

    Python3的执行过程

    python3 执行代码的过程

    1. 解释器找到代码文件,把代码字符串按文件头定义的编码加载到内存,转成unicode
    2. 把代码字符串按照语法规则进行解释,
    3. 所有的变量字符都会以unicode编码声明

    编码转换过程

     实际代码演示,在py3上 把你的代码以utf-8编写, 保存,然后在windows上执行

    python2

    它的默认编码是ASCII,想写中文,就必须声明文件头的coding为gbk or utf-8, 声明之后,python2解释器仅以文件头声明的编码去解释你的代码,加载到内存后,并不会主动帮你转为unicode,也就是说,你的文件编码是utf-8,加载到内存里,你的变量字符串就也是utf-8意味你以utf-8编码的文件,在windows是乱码。 

    因为只有2种情况 ,你的windows上显示才不会乱

    1. 字符串以GBK格式显示
    2. 字符串是unicode编码

    既然Python2并不会自动的把文件编码转为unicode存在内存里, 只能够你自己转。Py3 自动把文件编码转为unicode必定是调用了什么方法,这个方法就是,decode(解码) 和encode(编码)

    UTF-8 --> decode 解码 --> Unicode
    Unicode --> encode 编码 --> GBK / UTF-8 ..

    decode示例

     

    encode 示例

    编码解码规则

     

    Python只要出现各种编码问题,无非是哪里的编码设置出错了
    常见编码错误的原因有:

      • Python解释器的默认编码
      • Python源文件文件编码
      • Terminal使用的编码
      • 操作系统的语言设置

    掌握了编码之前的关系后,挨个排错就好啦 

    处理UnicodeEncodeError

    多数非 UTF 编解码器只能处理 Unicode 字符的一小部分子集。把文本转换成字节序列时,如果目标编码中没有定义某个字符,那就会抛出UnicodeEncodeError 异常,除非把 errors 参数传给编码方法或函
    数,对错误进行特殊处理。

    编码成字节序列:成功和错误处理

    >>> city = 'São Paulo'
    >>> city.encode('utf_8') ➊
    b'Sxc3xa3o Paulo'
    >>> city.encode('utf_16')
    b'xffxfeSx00xe3x00ox00 x00Px00ax00ux00lx00ox00'
    >>> city.encode('iso8859_1') ➋
    b'Sxe3o Paulo'
    >>> city.encode('cp437') ➌
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/.../lib/python3.4/encodings/cp437.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
    UnicodeEncodeError: 'charmap' codec can't encode character 'xe3' in
    position 1: character maps to <undefined>
    >>> city.encode('cp437', errors='ignore') ➍
    b'So Paulo'
    >>> city.encode('cp437', errors='replace') ➎
    b'S?o Paulo'
    >>> city.encode('cp437', errors='xmlcharrefreplace') ➏
    b'São Paulo'

    ❶ 'utf_?' 编码能处理任何字符串。
    ❷ 'iso8859_1' 编码也能处理字符串 'São Paulo'。
    ❸ 'cp437' 无法编码 'ã'(带波形符的“a”)。默认的错误处理方式'strict' 抛出 UnicodeEncodeError。
    ❹ error='ignore' 处理方式悄无声息地跳过无法编码的字符;这样做通常很是不妥。
    ❺ 编码时指定 error='replace',把无法编码的字符替换成 '?';数据损坏了,但是用户知道出了问题。
    ❻ 'xmlcharrefreplace' 把无法编码的字符替换成 XML 实体。

    处理UnicodeDecodeError

    不是每一个字节都包含有效的 ASCII 字符,也不是每一个字符序列都是有效的 UTF-8 或 UTF-16。因此,把二进制序列转换成文本时,如果假设是这两个编码中的一个,遇到无法转换的字节序列时会抛出
    UnicodeDecodeError。另一方面,很多陈旧的 8 位编码——如 'cp1252'、'iso8859_1' 和'koi8_r'——能解码任何字节序列流而不抛出错误,例如随机噪声。因此,如果程序使用错误的 8 位编码,解码过程悄无声息,而得到的是无用输出。

    把字节序列解码成字符串:成功和错误处理

    >>> octets = b'Montrxe9al'>>> octets.decode('cp1252') ➋
    'Montréal'
    >>> octets.decode('iso8859_7') ➌
    'Montrιal'
    >>> octets.decode('koi8_r') ➍
    'MontrИal'
    >>> octets.decode('utf_8') ➎
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 5:
    invalid continuation byte
    >>> octets.decode('utf_8', errors='replace') ➏
    'Montral'

    ❶ 这些字节序列是使用 latin1 编码的“Montréal”;'xe9' 字节对应“é”。
    ❷ 可以使用 'cp1252'(Windows 1252)解码,因为它是 latin1 的有效超集。
    ❸ ISO-8859-7 用于编码希腊文,因此无法正确解释 'xe9' 字节,而且没有抛出错误。
    ❹ KOI8-R 用于编码俄文;这里,'xe9' 表示西里尔字母“И”。
    ❺ 'utf_8' 编解码器检测到 octets 不是有效的 UTF-8 字符串,抛出UnicodeDecodeError。
    ❻ 使用 'replace' 错误处理方式,xe9 替换成了“ ”(码位是U+FFFD),这是官方指定的 REPLACEMENT CHARACTER(替换字符),表示未知字符。

    为了正确比较而规范化Unicode字符串

    因为 Unicode 有组合字符(变音符号和附加到前一个字符上的记号,打印时作为一个整体),所以字符串比较起来很复杂。

    例如,“café”这个词可以使用两种方式构成,分别有 4 个和 5 个码位,但是结果完全一样:

    >>> s1 = 'café'
    >>> s2 = 'cafeu0301'
    >>> s1, s2
    ('café', 'café')
    >>> len(s1), len(s2)
    (4, 5)
    >>> s1 == s2
    False

    U+0301 是 COMBINING ACUTE ACCENT,加在“e”后面得到“é”。在Unicode 标准中,'é' 和 'eu0301' 这样的序列叫“标准等价物”(canonical equivalent),应用程序应该把它们视作相同的字符。但
    是,Python 看到的是不同的码位序列,因此判定二者不相等。

    这个问题的解决方案是使用 unicodedata.normalize 函数提供的Unicode 规范化。这个函数的第一个参数是这 4 个字符串中的一个:'NFC'、'NFD'、'NFKC' 和 'NFKD'。下面先说明前两个。

    NFC(Normalization Form C)使用最少的码位构成等价的字符串,而NFD 把组合字符分解成基字符和单独的组合字符。这两种规范化方式都
    能让比较行为符合预期:

    >>> from unicodedata import normalize
    >>> s1 = 'café' # 把"e"和重音符组合在一起
    >>> s2 = 'cafeu0301' # 分解成"e"和重音符
    >>> len(s1), len(s2)
    (4, 5)
    >>> len(normalize('NFC', s1)), len(normalize('NFC', s2))
    (4, 4)
    >>> len(normalize('NFD', s1)), len(normalize('NFD', s2))
    (5, 5)
    >>> normalize('NFC', s1) == normalize('NFC', s2)
    True
    >>> normalize('NFD', s1) == normalize('NFD', s2)
    True

    西方键盘通常能输出组合字符,因此用户输入的文本默认是 NFC 形式。不过,安全起见,保存文本之前,最好使用 normalize('NFC',user_text) 清洗字符串。NFC 也是 W3C 的“Character Model for the
    World Wide Web: String Matching and Searching”规范形式。


    使用 NFC 时,有些单字符会被规范成另一个单字符。例如,电阻的单位欧姆(Ω)会被规范成希腊字母大写的欧米加。这两个字符在视觉上是一样的,但是比较时并不相等,因此要规范化,防止出现意外:

    >>> from unicodedata import normalize, name
    >>> ohm = 'u2126'
    >>> name(ohm)
    'OHM SIGN'
    >>> ohm_c = normalize('NFC', ohm)
    >>> name(ohm_c)
    'GREEK CAPITAL LETTER OMEGA'
    >>> ohm == ohm_c
    False
    >>> normalize('NFC', ohm) == normalize('NFC', ohm_c)
    True

    在另外两个规范化形式(NFKC 和 NFKD)的首字母缩略词中,字母 K表示“compatibility”(兼容性)。这两种是较严格的规范化形式,对“兼容字符”有影响。虽然 Unicode 的目标是为各个字符提供“规范的”码位,但是为了兼容现有的标准,有些字符会出现多次。例如,虽然希腊字母表中有“μ”这个字母(码位是 U+03BC,GREEK SMALL LETTER MU),但是 Unicode 还是加入了微符号 'µ'(U+00B5),以便与 latin1 相互转换。因此,微符号是一个“兼容字符”。在 NFKC 和 NFKD 形式中,各个兼容字符会被替换成一个或多个“兼容分解”字符,即便这样有些格式损失,但仍是“首选”表述——理想情况下,格式化是外部标记的职责,不应该由 Unicode 处理。下面举个例子。二分之一 '½'(U+00BD)经过兼容分解后得到的是三个字符序列'1/2';微符号 'µ'(U+00B5)经过兼容分解后得到的是小写字母'μ'(U+03BC)

    >>> from unicodedata import normalize, name
    >>> half = '½'
    >>> normalize('NFKC', half)
    '1⁄2'
    >>> four_squared = ''
    >>> normalize('NFKC', four_squared)
    '42'
    >>> micro = 'μ'
    >>> micro_kc = normalize('NFKC', micro)
    >>> micro, micro_kc
    ('μ', 'μ')
    >>> ord(micro), ord(micro_kc)
    (181, 956)
    >>> name(micro), name(micro_kc)
    ('MICRO SIGN', 'GREEK SMALL LETTER MU')

    使用 '1/2' 替代 '½' 可以接受,微符号也确实是小写的希腊字母'µ',但是把 '4²' 转换成 '42' 就改变原意了。某些应用程序可以把'4²' 保存为 '4<sup>2</sup>',但是 normalize 函数对格式一无所
    知。因此,NFKC 或 NFKD 可能会损失或曲解信息,但是可以为搜索和索引提供便利的中间表述:用户搜索 '1 / 2 inch' 时,如果还能找到包含 '½ inch' 的文档,那么用户会感到满意

    使用 NFKC 和 NFKD 规范化形式时要小心,而且只能在特
    殊情况中使用,例如搜索和索引,而不能用于持久存储,因为这两
    种转换会导致数据损失。

    大小写折叠

    大小写折叠其实就是把所有文本变成小写,再做些其他转换。这个功能
    由 str.casefold() 方法(Python 3.3 新增)支持。
    对于只包含 latin1 字符的字符串 s,s.casefold() 得到的结果与
    s.lower() 一样,唯有两个例外:微符号 'µ' 会变成小写的希腊字
    母“μ”(在多数字体中二者看起来一样);德语 Eszett(“sharp s”,ß)
    会变成“ss”。

    >>> micro = 'μ'
    >>> name(micro)
    'MICRO SIGN'
    >>> micro_cf = micro.casefold()
    >>> name(micro_cf)
    'GREEK SMALL LETTER MU'
    >>> micro, micro_cf
    ('μ', 'μ')
    >>> eszett = 'ß'
    >>> name(eszett)
    'LATIN SMALL LETTER SHARP S'
    >>> eszett_cf = eszett.casefold()
    >>> eszett, eszett_cf
    ('ß', 'ss')

    自 Python 3.4 起,str.casefold() 和 str.lower() 得到不同结果的有
    116 个码位。Unicode 6.3 命名了 110 122 个字符,这只占 0.11%。
    与 Unicode 相关的任何问题一样,大小写折叠是个复杂的问题,有很多
    语言上的特殊情况,但是 Python 核心团队尽力提供了一种方案,能满足
    大多数用户的需求。




    转自https://www.cnblogs.com/alex3714/articles/7550940.html

  • 相关阅读:
    hdu5593--ZYB's Tree(树形dp)
    poj1637--Sightseeing tour(最大流)
    Educational Codeforces Round 81 (Rated for Div. 2)
    【cf1286B】B. Numbers on Tree(贪心)
    【cf1285E】E. Delete a Segment(vector+二分)
    【cf1293E】E.Xenon's Attack on the Gangs(dp)
    Codeforces Round #609 (Div. 2)
    Educational Codeforces Round 78 (Rated for Div. 2)
    【bzoj4671】异或图(容斥+斯特林反演+线性基)
    【bzoj5339】[TJOI2018]教科书般的亵渎(拉格朗日插值/第二类斯特林数)
  • 原文地址:https://www.cnblogs.com/chenxuming/p/9426544.html
Copyright © 2020-2023  润新知