python高級(四)—— 文本和位元組序列(編碼問題)

来源:https://www.cnblogs.com/liao-sir/archive/2018/02/28/8480474.html
-Advertisement-
Play Games

本文主要內容 字元 位元組 結構體和記憶體視圖 字元和位元組之間的轉換——編解碼器 BOM鬼符 明天繼續。。。 python高級——目錄 文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高級 字元 此時只用記住在pyth ...


本文主要內容

字元

位元組

結構體和記憶體視圖

字元和位元組之間的轉換——編解碼器

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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 效果圖: 思路: 1, 繪製canvas畫布,進行基礎設置 2.繪製一個矩形 3.設置驗證碼的隨機數 4.設置驗證碼隨機數的隨機顏色 5.繪製隨機干擾線 6,繪製隨機干擾點 經過以上六個步驟,驗證碼的雛形就做好了 7.旋轉驗證碼中的隨機數(這部分在章節內詳細說明) 8.重新獲取驗證碼 縷清思路,然後 ...
  • 在JavaScript Math 對象中: sin() 方法可返回一個數字的正弦。 tan() 方法可返回一個表示某個角的正切的數字。 參數x是必需。一個以弧度表示的角。將角度乘以 0.017453293 (2PI / 360)即可轉換為弧度(即 角度 Math.PI / 180)。 cos() 方 ...
  • 1.首先來說下cookie的作用 我們在瀏覽器中,經常涉及到數據的交換,比如你登錄郵箱,登錄一個頁面。我們經常會在此時設置30天內記住我,或者自動登錄選項。那麼它們是怎麼記錄信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP伺服器設置的,保存在瀏覽器中,但HTTP協議是一種無狀態協 ...
  • 表單元素file設置隱藏,通過其他元素打開: .imgfile為input file JS部分: 一般處理程式部分: ...
  • 1、Element.scrollIntoView() 該方法讓當前元素滾動到瀏覽器視窗的可是區域內; ...
  • 在頁面排版中,經常遇到長英文單詞溢出段落容器的情況,如何解決該問題?不讓測試妹妹來騷擾你呢?在CSS中提到單詞斷行,自然就會想到word-break和word-wrap。具體差別對比請查看演示及說明。 ...
  • 一、重構 1、重構變數 修改變數名稱,即重命名。快捷鍵 Shift + F6 ,位於 Refactor 中。 2、重構方法 可以增加變數個數。快捷鍵 Ctrl + F6 ,位於 Refactor 中。 二、抽取 1、抽取變數 抽取變數的快捷鍵 Ctrl +Alt + V,位於 Refactor 中的 ...
  • 一、前言 Python 面向對象中有繼承這個概念,初學時感覺很牛逼,裡面也有個super類,經常見到,最近做一些題才算是理解了。特地記錄分享給後來研究的小伙伴,畢竟現在小學生都開始學了(滑稽臉) 二、代碼 直接上乾貨,能把下麵一個問題全答對,後面就不用看了。 當然,直接運行就有答案了,還是要仔細想一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...