python高級(四)—— 文本和字節序列(編碼問題)

本文主要內容

字符html

字節python

結構體和內存視圖git

字符和字節之間的轉換——編解碼器github

BOM鬼符express

    標準化Unicode字符串編程

  Unicode文本排序數組

 

python高級——目錄函數

文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高級post

 

字符

'''
    字符編碼問題是常常困擾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,這是人類可以看懂的語言。ui

 

字節

'''
    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'abc\xe4\xbd\xa0\xe5\xa5\xbd'

    b2 = bytearray('abc你好', encoding='utf8')
    print(b2)          # bytearray(b'abc\xe4\xbd\xa0\xe5\xa5\xbd')

    # 切片(提示:序列均可以切片)
    print(b1[3:5])     # b'\xe4\xbd'
    print(b2[3:5])     # bytearray(b'\xe4\xbd')

    # 使用列表取值的方法試試
    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'abc\xe4\xbd\xa0\xe5\xa5\xbd')
    b2[2] = 78
    print(id(b2), b2)      # 4373768376 bytearray(b'abN\xe4\xbd\xa0\xe5\xa5\xbd')

    # 將b2轉換成字符串看看
    print(b2.decode('utf8'))  # abN你好
                              # 注意,這裏之因此可以用utf8轉成unicode,是由於N的ascii碼和utf8一致
    b2.extend(bytearray('添加的內容', encoding='utf8'))  # 既然是可變序列,bytearray固然擁有通常的序列的方法
    print(id(b2), b2)         # 4373768376 bytearray(b'abN\xe4\xbd\xa0\xe5\xa5\xbd\xe6\xb7\xbb\xe5\x8a\xa0\xe7\x9a\x84\xe5\x86\x85\xe5\xae\xb9')

    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'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x02\x00\x1c\x00\x1c\x00\x00'
    print(struct.unpack(fmt, img[:10]))   # (b'\xff\xd8\xff', b'\xe0\x00\x10', 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='\t')
    '''
                        gbk      b'\xc4\xe3\xba\xc3'
                        utf8    b'\xe4\xbd\xa0\xe5\xa5\xbd'
                        utf16    b'\xff\xfe`O}Y'
    '''
    # 我們再來解碼
    print(b'\xc4\xe3\xba\xc3'.decode('gbk'))            # 你好
    print(b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf8'))   # 你好
    print(b'\xff\xfe`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'Montr\xe9al'
    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 = 'cafe\u0301'
print(s1, s2)     # café café
print(s1 == s2)   # False

'''
    咱們發現café能夠用'café'和'cafe\u0301'兩種方式表示,這個詞對於人來講是同樣的,可是這兩種表示對於計算機來講確實不同的
    向這樣的序列叫"標準等價物",在計算機中存儲的值不相等,但應用程序應該認爲相等。
    
    要解決這個問題,須要用到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'caf\xc3\xa9' b'cafe\xcc\x81'
    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'caf\xc3\xa9' b'caf\xc3\xa9'

    # "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高級——目錄

相關文章
相關標籤/搜索