zoukankan      html  css  js  c++  java
  • python编码与解码

    基本概念

    字符:表示数据和信息的字母、数字或其他符号。在电子计算机中,每一个字符与一个二进制编码相对应。

    字符的标识(码位):是0-1114111的数字,在Unicode标准中以4-6个十六进制数字表示,而且加前缀“U+”。例如,字母A的码位是U+0041,欧元符号的码位是U+20AC.

    字符的具体表述取决于所用的编码。编码是在码位和字节序列之间转换时使用的算法。在UTF-8编码中,A(U+0041)的码位编码成单个字节x41,而在UTF-16LE编码中编码成两个字节x41x00。欧元符号(U+20AC)在UFT-8编码中是三个字节xe2x82xac,而在UTF-16LE编码中编码成两个字节xacx20。

    编码:把码位转换成字节序列(通俗来说:把字符串转换成用于存储或传输的字节序列,python中是.encode())

    解码:把字节序列转换成码位(通俗来说:把字节序列转换成人类可读的文本字符串,python中是.decode())

    >>> s = 'café'
    >>> len(s) # Unicode字符数量
    4
    >>> b = s.encode('utf8') # 编码为bytes
    >>> b
    b'cafxc3xa9' 
    >>> len(b) # 字节数
    5
    >>> b.decode('utf8') # 解码
    'café

    字节概要

    新的二进制序列类型在很多方面与 Python 2 的 str 类型不同。首先要知道,Python 内置了两种基本的二进制序列类型:Python 3 引入的不可变bytes 类型和 Python 2.6 添加的可变 bytearray 类型。(Python 2.6 也引入了 bytes 类型,但那只不过是 str 类型的别名,与 Python 3 的bytes 类型不同。)

      bytes 或 bytearray 对象的各个元素是介于 0~255(含)之间的整数,而不像 Python 2 的 str 对象那样是单个的字符。然而,二进制序列的切片始终是同一类型的二进制序列,包括长度为 1 的切片,如示例:

    >>> cafe = bytes('café', encoding='utf_8')
    >>> cafe
    b'cafxc3xa9'
    >>> cafe[0]
    >>> cafe[:1]
    b'c'
    >>> cafe_arr = bytearray(cafe)
    >>> cafe_arr
    bytearray(b'cafxc3xa9')
    >>> cafe_arr[-1:]
    bytearray(b'xa9')

    二进制序列有个类方法是 str 没有的,名为 fromhex,它的作用是解析十六进制数字对(数字对之间的空格是可选的),构建二进制序列:

    >>> bytes.fromhex('31 4B CE A9')
    b'1Kxcexa9'

    python基本编码解码器

    python自带了超过100种编码解码器,用于在文本和字节中转换。每个编码解码器有一个名称,如'utf-8',通常有几个别名如'utf-8'别名有'utf8'、''utf_8、'U8'。

    ISO-8859-1(latin1):该编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。

    cp1252ISO-8859-1的超集,添加了一些有用的符号。

    cp437:IBM PC最初的字符集,包含框图符号,于ISO-8859-1不兼容。

    gb2312:简体中文陈旧标准,亚洲语言使用较广泛的多字节编码。

    utf-8:目前Web中最常见的8位编码;与ASCII兼容

    utf-16le:UTF-16的16位编码方案的一种形式;所有UTF-16支持转义序列表示超过U+FFFF的码位。

    编码存在的问题

    处理UnicodeError

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

    city = 'São Paulo'
    
    u8 = city.encode('utf_8')
    print('utf-8:', u8)
    #结果:  utf-8: b'Sxc3xa3o Paulo'      'utf_?' 编码能处理任何字符串

    u16 = city.encode('utf_16') print('utf-16:', u16) #结果:  utf-16: b'xffxfeSx00xe3x00ox00 x00Px00ax00ux00lx00ox00'  
    iso = city.encode('iso8859_1') print('iso:', iso) #结果:  iso: b'Sxe3o Paulo'  'iso8859_1' 编码也能处理字符串 'São Paulo
    cpr37 = city.encode('cp437')   #结果:  报错      'cp437' 无法编码 'ã'(带波形符的“a”)
    cp_ig = city.encode('cp437', errors='ignore') print('cp ignore:', cp_ig) #结果:  cp ignore: b'So Paulo'
    cp_rp = city.encode('cp437', errors='replace') print('cp replace:', cp_rp) #结果:  cp replace: b'S?o Paulo'

    如上处理策略:

    1.error='ignore' 处理方式悄无声息地跳过无法编码的字符;这样做通常很是不妥

    2.编码时指定error='replace',把无法编码的字符替换成'?';数据损坏了,但是用户知道出现了问题

    3.把errors参数注册额外字符串,方法是把一个名称和一个错误处理函数传递给codes.register_error函数。(参考python标准库)

    处理UnicodeDecodeError

    不是每个字节都包含有效的ASCALL字符,也不是每个字符序列都是有效的UTF-8或UTF16。因此,把二进制序列转换为文本时,如果假设是这两个编码中的一个,遇到无法转换的字节序列时会抛出UnicodeDecodeError。

    octets = b'Montrxe9al'     #iso8859_1编码
    
    cp1252 = octets.decode('cp1252')   
    print(cp1252)
    #结果:Montréal    正确解码   cp1252是iso8859_1的超集
    
    iso = octets.decode('iso8859_7')
    print(iso)
    #结果:Montrιal   错误解码
    
    koi8_r = octets.decode('koi8_r')
    print(koi8_r)
    #结果:MontrИal   错误解码
    
    #utf_8 = octets.decode('utf-8')
    print(utf_8)
    #结果:异常    不是有效的utf-8字符串
    
    new_utf_8 = octets.decode('utf-8', errors='replace')
    print(new_utf_8)
    #结果:Montr�al    官方指定的错误处理方式

    处理SyntaxError

    python3默认使用utf-8编码源码,如果加载的模块中包含utf-8之外的数据,而且没有声明编码,则会抛出SyntaxError异常。linux和OS X系统大都使用utf-8,因此在打开windows系统中使用cp1252编码的.py文件可能发生这种情况。

    为了修正这个问题,可以在文件顶部添加一个神奇的coding注释,  coding:cp1252

    判断文件字节序列编码

    使用命令行工具chardetect(python库Chardet提供的)

     处理文本文件

    处理文本的最佳实践是“Unicode 三明治”(如图下图所示)。 意思是,要尽早把输入(例如读取文件时)的字节序列解码成字符串。这种三明治中的“肉片”是程序的业务逻辑,在这里只能处理字符串对象。在其他处理过程中,一定不能编码或解码。对输出来说,则要尽量晚地把字符串编码成字节序列。多数 Web 框架都是这样做的,使用框架时很少接触字节序列。例如,在 Django 中,视图应该输出 Unicode 字符串;Django 会负责把响应编码成字节序列,而且默认使用 UTF-8 编码。

    #打开一个文件cafe.txt并写入内容,w是对文件的模式操作(写操作), encoding是对文件操作的编码
    fp = open('cafe.txt', 'w', encoding='utf_8')
    fp_len = fp.write('café')
    print('fp的io信息:', fp)
    print('写入到文件中内容的长度:', fp_len)
    fp.close()
    
    #获取文件的内容
    fp2 = open('cafe.txt')
    print('fp2的io信息:', fp2)
    '''
    因为和上面的写入的编码不同,所以直接以默认的编码打开,无法处理é而引发异常
    '''
    #print(fp2.read())
    fp2.close()
    
    #解决fp2无法或许文件内容的方法指定打开的时候编码
    fp3 = open('cafe.txt', encoding='utf-8')
    print('fp3的io信息:', fp3)
    print('fp3中的文件内容:', fp3.read())
    fp3.close()
    
    fp4 = open('cafe.txt', 'rb')
    print('fp4的io信息:', fp4)
    print('fp4的文件内容:', fp4.read().decode('utf-8'))
    fp4.close()
    
    #另外一种不太可取的解决方案, errors可以设置成replace或者ignore
    fp5 = open('cafe.txt', 'r', errors='ignore')
    print('fp5的io信息:', fp5)
    print('fp5的文件内容:', fp5.read())

     以上代码结果:

    fp的io信息: <_io.TextIOWrapper name='cafe.txt' mode='w' encoding='utf_8'>
    写入到文件中内容的长度: 4
    fp2的io信息: <_io.TextIOWrapper name='cafe.txt' mode='r' encoding='US-ASCII'>
    fp3的io信息: <_io.TextIOWrapper name='cafe.txt' mode='r' encoding='utf-8'>
    fp3中的文件内容: café
    fp4的io信息: <_io.BufferedReader name='cafe.txt'>
    fp4的文件内容: café
    fp5的io信息: <_io.TextIOWrapper name='cafe.txt' mode='r' encoding='US-ASCII'>
    fp5的文件内容: caf

    Unicode规范化

    使用unicodedata.normalize提供的nuicode规范化

    NFC使用最少码位构成等价的字符串,而NFD把组合字符分解成基字符和单独的组合字符

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

     实际  é   ===    'eu0301'  是成立的,而Python 看到的是不同的码位序列,因此判定二者不相等,则需要使用NFC或NFD。

    from unicodedata import normalize
    
    
    s1 = 'café' # 把"e"和重音符组合在一起
    s2 = 'cafeu0301' # 分解成"e"和重音符
    print('s1和s2的长度:', len(s1), len(s2))
    
    print('NFC标准化处理以后的s1,s2的长度:', len(normalize('NFC', s1)), len(normalize('NFC', s2)))
    print('NFD标准化处理以后的s1,s2的长度:', len(normalize('NFD', s1)), len(normalize('NFD', s2)))
    print(normalize('NFC', s1), normalize('NFC', s2))


    #结果:
    s1和s2的长度: 4 5
    NFC标准化处理以后的s1,s2的长度: 4 4
    NFD标准化处理以后的s1,s2的长度: 5 5
    café café
  • 相关阅读:
    apache .htaccess文件详解和配置技巧总结
    看懂UML类图和时序图
    你必须了解的Session的本质
    php中fopen不能创建中文文件名文件的问题
    RabbitMQ消费服务关掉时会删除exchange,导致生成服务发布内容失败
    fastjson 调用JSONObject.toJSON(),如果是解析泛型对象会报OutOfMemoryError错误
    docker启动springboot项目,中文打印乱码
    Spring事务嵌套抛异常org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
    Springboot + SLF4j + Log4j2 打印异常日志时,耗时要5-6秒
    SVN设置全局忽略提交文件或者目录
  • 原文地址:https://www.cnblogs.com/lht-record/p/10235937.html
Copyright © 2011-2022 走看看