zoukankan      html  css  js  c++  java
  • python高级(四)—— 文本和字节序列(编码问题)

    本文主要内容

    字符

    字节

    结构体和内存视图

    字符和字节之间的转换——编解码器

    BOM鬼符

        标准化Unicode字符串

      Unicode文本排序

    python高级——目录

    文中代码均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高级

    字符

    '''
        字符编码问题是经常困扰python编程人员的问题,我在编写爬虫的过程中也经常遇到这个头疼的事。
    
        从python3开始,明确区分了人类语言(文本字符串)和机器语言(二进制字节),咱们先说文本字符串
        开始之前,得对"字符"进行定义:
            字符:Unicode字符,从python3的str对象中获取的元素是Unicode字符
            字符串:字符串就是一个字符序列(这里对于(一)中内容相呼应)
    
    '''
    
    
    if __name__ == "__main__":
        # 创建字符
        s1 = str('a')
        s2 = 'b'
        s3 = u'c'
        print(s1, s2, s3)      # a b c

       

      此时只用记住在python3中字符就是unicode,也就是str是unicode,这是人类能够看懂的语言。

    字节

    '''
        python3中内置有两种基本的二进制序列类型:不可变的bytes和可变bytearray
            (1)bytes和bytearray的各个元素是介于0~255(8个bit)之间的整数;
            (2)二进制序列的切片始终是同一类型的二进制序列
    '''
    
    
    if __name__ == "__main__":
        # 创建bytes 和 bytearray
        b1 = bytes('abc你好', encoding='utf8')      # 关于encode稍后会说,不知道有没有人和我一样总是将编码与解码的方向混淆
        print(b1)          # b'abcxe4xbdxa0xe5xa5xbd'
    
        b2 = bytearray('abc你好', encoding='utf8')
        print(b2)          # bytearray(b'abcxe4xbdxa0xe5xa5xbd')
    
        # 切片(提示:序列都可以切片)
        print(b1[3:5])     # b'xe4xbd'
        print(b2[3:5])     # bytearray(b'xe4xbd')
    
        # 使用列表取值的方法试试
        print(b1[3])       # 228 此时取出来的就不是字节序列了,而是一个元素
        for _ in b1:
            print(_, end=',')   # 97,98,99,228,189,160,229,165,189,      这都是8bit的整数
    
        # bytes的不可变 vs. bytearray的可变
    
        # b1[3] = 160           # 报错:'bytes' object does not support item assignment
        print(id(b2), b2)      # 4373768376 bytearray(b'abcxe4xbdxa0xe5xa5xbd')
        b2[2] = 78
        print(id(b2), b2)      # 4373768376 bytearray(b'abNxe4xbdxa0xe5xa5xbd')
    
        # 将b2转换成字符串看看
        print(b2.decode('utf8'))  # abN你好
                                  # 注意,这里之所以能够用utf8转成unicode,是因为N的ascii码和utf8一致
        b2.extend(bytearray('添加的内容', encoding='utf8'))  # 既然是可变序列,bytearray当然拥有一般的序列的方法
        print(id(b2), b2)         # 4373768376 bytearray(b'abNxe4xbdxa0xe5xa5xbdxe6xb7xbbxe5x8axa0xe7x9ax84xe5x86x85xe5xaexb9')
    
        print(b2.decode('utf8'))  # abN你好添加的内容
    
        # PS:大家可以将二进制序列当成列表,元素就是ascii编码(0~255)

    结构体和内存视图

    '''
        struct可以从二进制序列中提取结构化信息。
        struct模块提供了一些函数,可以将打包的字节序列转换成不同类型字段组成的元组;还有一些函数用于执行反向转换。
        struct模块可以处理bytes、bytearray、memoryview对象。
    '''
    
    import struct
    
    if __name__ == "__main__":
        # memoryview类用于共享内存,可以访问其他二进制序列、打包的数组和缓冲中的数据切片,该操作无需赋值字节序列
        fmt = '<3s3sHH'   # 设置格式,< 是小字节序,3s3s是两个3字节序列,HH是两个16位二进制整数
    
        with open('L3_图_python.jpg', 'rb') as f:     # 需要在github中下载后运行
            img = memoryview(f.read())
    
        print(bytes(img[:10]))    # b'xffxd8xffxe0x00x10JFIFx00x01x01x02x00x1cx00x1cx00x00'
        print(struct.unpack(fmt, img[:10]))   # (b'xffxd8xff', b'xe0x00x10', 17994, 17993)  :拆包
    
        del img

    字符和字节之间的转换——编解码器

    '''
        python自带有超过100中编解码器,用于在字符串和字节之间相互转换。
        每个编码都有多个名称,例如'utf_8'、'utf8'、'utf-8'、'U8',这些都可以传递给open()、str.encode()、bytes.decode()中的
        encoding参数
    '''
    
    
    if __name__ == "__main__":
        # 看看不同的编码效果
        for codec in ['gbk', 'utf8', 'utf16']:
            print(codec, "你好".encode(codec), sep='	')
        '''
                            gbk      b'xc4xe3xbaxc3'
                            utf8    b'xe4xbdxa0xe5xa5xbd'
                            utf16    b'xffxfe`O}Y'
        '''
        # 咱们再来解码
        print(b'xc4xe3xbaxc3'.decode('gbk'))            # 你好
        print(b'xe4xbdxa0xe5xa5xbd'.decode('utf8'))   # 你好
        print(b'xffxfe`O}Y'.decode('utf16'))              # 你好
    '''
        遇到编码问题一般很烦躁,下面来看看一般怎么解决编码问题。
        (1)UnicodeEncodeError
         (2) UnicodeDecodeError
    '''
    
    
    
    if __name__ == "__main__":
        # (1)UnicodeEncodeError
        # 使用errors参数
        s1 = "hello,你长胖啦".encode('latin-1', errors='ignore')
        print(s1)   # b'hello'    使用 errors='ignore' 忽略了无法编码的字符
    
        s2 = "hello,你长胖啦".encode('latin-1', errors='replace')
        print(s2)   # b'hello?????'    使用errors='replace'将无法编码的字符用问好代替
    
        s3 = "hello,你长胖啦".encode('latin-1', errors='xmlcharrefreplace')
        print(s3)   # b'hello&#65292;&#20320;&#38271;&#32982;&#21862;'  使用errors='xmlcharrefreplace'将无法编码的内容替换成XML实体
    
        # (2) UnicodeDecodeError
        # 乱码字符称为鬼符,以下实例演示出现鬼符的情况
    
        s4 = b'Montrxe9al'
        print(s4.decode('cp1252'))    # Montréal
        print(s4.decode('iso8859_7')) # Montrιal
        print(s4.decode('koi8_r'))    # MontrИal
        #print(s4.decode('utf8'))      # 报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 5: invalid continuation byte
        print(s4.decode('utf8', errors='replace'))   # Montr�al
        '''
            大多数人都遇到过乱码问题,并且可能总是调试不成功,这可能是各种程序之间的编码不匹配
            以下代码引用自<流畅的python>,可以用来查看当前环境的一些默认编码     
        '''
    
        # -*- coding: utf-8 -*-
    
        import sys, locale
    
        expressions = """
                locale.getpreferredencoding()
                type(my_file)
                my_file.encoding
                sys.stdout.isatty()
                sys.stdout.encoding
                sys.stdin.isatty()
                sys.stdin.encoding
                sys.stderr.isatty()
                sys.stderr.encoding
                sys.getdefaultencoding()
                sys.getfilesystemencoding()
            """
    
        my_file = open('dummy', 'w')
    
        for expression in expressions.split():
            value = eval(expression)
            print(expression.rjust(30), '->', repr(value))
    
        '''
            我电脑运行结果如下:
            (' locale.getpreferredencoding()', '->', "'UTF-8'")
            ('                 type(my_file)', '->', "<type 'file'>")
            ('              my_file.encoding', '->', 'None')
            ('           sys.stdout.isatty()', '->', 'True')
            ('           sys.stdout.encoding', '->', "'UTF-8'")
            ('            sys.stdin.isatty()', '->', 'True')
            ('            sys.stdin.encoding', '->', "'UTF-8'")
            ('           sys.stderr.isatty()', '->', 'True')
            ('           sys.stderr.encoding', '->', "'UTF-8'")
            ('      sys.getdefaultencoding()', '->', "'ascii'")
            ('   sys.getfilesystemencoding()', '->', "'utf-8'")
        '''

    BOM鬼符

     ''' 关于BOM的内容比较底层,以下内容全部选自<流畅的python> ''' 

    标准化Unicode字符串

    '''
        初一看这个标题可能会有点蒙,难道Unicode本身还不够标准么?
        先看看以下的例子:
    '''
    s1 = 'café'
    s2 = 'cafeu0301'
    print(s1, s2)     # café café
    print(s1 == s2)   # False
    
    '''
        我们发现café可以用'café'和'cafeu0301'两种方式表示,这个词对于人来说是一样的,但是这两种表示对于计算机来说确实不一样的
        向这样的序列叫"标准等价物",在计算机中存储的值不相等,但应用程序应该认为相等。
        
        要解决这个问题,需要用到unicodedata.nomalize函数,它的第一个参数可以选择这四种形式的一个:"NFC"、"NFD"和"NFKC"、"NFKD"
            "NFC":使用最少的码为构成等价的字符串
            "NFD":把组合的字符分割成基本字符和单独的组合字符
            
            "NFKC"&"NFKD":这两种是较严格的规范形式,对"兼容字符有影响"
    '''
    
    from unicodedata import normalize
    
    if __name__ == "__main__":
        # "NFC" & "NFD"
        print(s1.encode('utf8'), s2.encode('utf8'))   # b'cafxc3xa9' b'cafexccx81'
        s1 = normalize("NFC", s1)
        s2 = normalize("NFC", s2)
        print(s1, s2)    # café café
        print(s1 == s2)  # True
        print(s1.encode('utf8'), s2.encode('utf8'))   # b'cafxc3xa9' b'cafxc3xa9'
    
        # "NFKC"&"NFKD"
        # 这两种方式会损失信息,所以不建议使用,除非一些特殊情况,比如搜索和索引中
        s3 = '½'
        print(normalize('NFKC', s3))    # 1⁄2    将½转换成了1⁄2
        s4 = ''
        print(normalize('NFKC', s4))    # TM     将™转换成了TM
        '''
        另外,如果比较的时候不区分大小写,建议使用str.casefold(), 它与lower基本一致,其中大约有116个特殊的字符结果不同
        '''
        s5 = 'AKJkakshfKHDSdshfKSDShKkHkjhKJkgJhgJHkkHkjhJKhKJhK'
        print(s5.casefold())    # akjkakshfkhdsdshfksdshkkhkjhkjkgjhgjhkkhkjhjkhkjhk

    Unicode文本排序

    '''
        python比较序列时,会一一比较其中的元素。对于字符来说,比较的是其码位,主要是比较的ascii码;
        非ascii文本的标准排序方式是使用locale.strxfrm函数,但是使用这个函数必须事先设定区域,但有些操作系统不支持,并且改变区域设置并不十分合适
    
        建议使用pyuca.Collator.sort_key方法进行排序
    '''
    
    
    
    if __name__ == "__main__":
        # python默认的排序
        fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
        print(sorted(fruits))    # ['acerola', 'atemoia', 'açaí', 'caju', 'cajá']
    
                                # 但正确排序应该是:['açaí','acerola', 'atemoia', 'cajá', 'caju']
    
        # 使用pyuca.Collator.sort_key
    
        import pyuca
        print(sorted(fruits, key = pyuca.Collator().sort_key))   # ['açaí', 'acerola', 'atemoia', 'cajá', 'caju']
    
        # pyuca可以将自定义排序表路径传递给Collator()构造方法,pyuca默认使用自带的allkeys.txt

    python高级系列文章目录

    python高级——目录

  • 相关阅读:
    【9018:2221】[伪模板]可持久化线段树
    【9018:2208】可持久化线段树2
    【9018:2207】可持久化线段树1
    【POJ2187】Beauty Contest
    2017/11/22模拟赛
    2017/11/3模拟赛
    [AtCoder 2702]Fountain Walk
    [AtCoder3856]Ice Rink Game
    20170910模拟赛
    20170906模拟赛
  • 原文地址:https://www.cnblogs.com/liao-sir/p/8480474.html
Copyright © 2011-2022 走看看