Python3標準庫:zlib GNUzlib壓縮

来源:https://www.cnblogs.com/liuhui0308/archive/2020/03/27/12583668.html
-Advertisement-
Play Games

1. zlib GNUzlib壓縮 zlib模塊為GNU項目zlib壓縮庫中的很多函數提供了底層介面。 1.1 處理記憶體中的數據 使用zlib最簡單的方法要求把所有將要壓縮或解壓縮的數據存放在記憶體中。 import zlib import binascii original_data = b'Thi ...


1. zlib GNUzlib壓縮

zlib模塊為GNU項目zlib壓縮庫中的很多函數提供了底層介面。

1.1 處理記憶體中的數據

使用zlib最簡單的方法要求把所有將要壓縮或解壓縮的數據存放在記憶體中。

import zlib
import binascii

original_data = b'This is the original text.'
print('Original     :', len(original_data), original_data)

compressed = zlib.compress(original_data)
print('Compressed   :', len(compressed),
      binascii.hexlify(compressed))

decompressed = zlib.decompress(compressed)
print('Decompressed :', len(decompressed), decompressed)

compress()和decompress()函數都取一個位元組序列參數,並且返回一個位元組序列。

從前面的例子可以看到,少量數據的壓縮版本可能比未壓縮的版本還要大。具體的結果取決於輸入數據,不過觀察小數據集的壓縮開銷很有意思。 

import zlib

original_data = b'This is the original text.'

template = '{:>15}  {:>15}'
print(template.format('len(data)', 'len(compressed)'))
print(template.format('-' * 15, '-' * 15))

for i in range(5):
    data = original_data * i
    compressed = zlib.compress(data)
    highlight = '*' if len(data) < len(compressed) else ''
    print(template.format(len(data), len(compressed)), highlight)

輸出中的*突出顯示了哪些行的壓縮數據比未壓縮版本占用的記憶體更多。

zlib支持不同的壓縮級別,允許在計算成本和空間縮減量之間有所平衡。預設壓縮級別zlib.Z_DEFAULT_COMPRESSION為-1,這對應一個硬編碼值,表示性能和壓縮結果之間的一個折中。當前這對應級別6。

import zlib

input_data = b'Some repeated text.\n' * 1024
template = '{:>5}  {:>5}'

print(template.format('Level', 'Size'))
print(template.format('-----', '----'))

for i in range(0, 10):
    data = zlib.compress(input_data, i)
    print(template.format(i, len(data)))

壓縮級別為0意味著根本沒有壓縮。級別9要求的計算最多,同時會生成最小的輸出。如下麵的例子,對於一個給定的輸入,可以多個壓縮級別得到的空間縮減量是一樣的。

1.2 增量壓縮與解壓縮

這種記憶體中的壓縮方法有一些缺點,主要是系統需要有足夠的記憶體,可以在記憶體中同時駐留未壓縮和壓縮版本,因此這種方法對於真實世界的用例並不實用。另一種方法是使用Compress和Decompress對象以增量方式處理數據,這樣就不需要將整個數據集都放在記憶體中。

import zlibimport binascii

compressor = zlib.compressobj(1)

with open('lorem.txt','rb') as input:
    while True:
        block = input.read(64)
        if not block:
            break
        compressed = compressor.compress(block)
        if compressed:
            print('Compressed: {}'.format(
                binascii.hexlify(compressed)))
        else:
            print('buffering...')
    remaining = compressor.flush()
    print('Flushed: {}'.format(binascii.hexlify(remaining)))

這個例子從一個純文本文件讀取小數據塊,並把這個數據集傳至compress()。壓縮器維護壓縮數據的一個記憶體緩衝區。由於壓縮演算法依賴於校驗和以及最小塊大小,所以壓縮器每次接收更多輸入時可能並沒有準備好返回數據。如果它沒有準備好一個完整的壓縮塊,那便會返回一個空位元組串。當所有

1.3 混合內容流

在壓縮和未壓縮數據混合在一起的情況下,還可以使用decompressobj()返回的Decompress類。

import zlib

lorem = open('lorem.txt','rb').read()
compressed = zlib.compress(lorem)
combined = compressed +lorem

decompressor = zlib.decompressobj()
decompressed = decompressor.decompress(combined)

decompressed_matches = decompressed == lorem
print('Decompressed matches lorem:',decompressed_matches)

unused_matches = decompressor.unused_data == lorem
print('Unused data matches lorem:',unused_matches)

解壓縮所有數據後,unused_data屬性會包含未用的所有數據。

1.4 校驗和

除了壓縮和解壓縮函數,zlib還包括兩個用於計算數據的校驗和的函數,分別是adler32()和crc32()。這兩個函數計算出的校驗和都不能認為是密碼安全的,它們只用於數據完整性驗證。

import zlib

data = open('lorem.txt','rb').read()

cksum = zlib.adler32(data)
print('Adler32: {:12d}'.format(cksum))
print('       : {:12d}'.format(zlib.adler32(data,cksum)))

cksum = zlib.crc32(data)
print('CRC-32: {:12d}'.format(cksum))
print('       : {:12d}'.format(zlib.crc32(data,cksum)))

這兩個函數取相同的參數,包括一個包含數據的位元組串和一個可選值,這個值可作為校驗和的起點。這些函數會返回一個32位有符號整數值,這個值可以作為一個新的起點參數再傳回給後續的調用,以生成一個動態變化的校驗和。

1.5 壓縮網路數據

下一個代碼清單中的伺服器使用流壓縮器來響應文件名請求,它將文件的一個壓縮版本寫至與客戶通信的套接字中。

import zlib
import logging
import socketserver
import binascii

BLOCK_SIZE = 64


class ZlibRequestHandler(socketserver.BaseRequestHandler):

    logger = logging.getLogger('Server')

    def handle(self):
        compressor = zlib.compressobj(1)

        # Find out what file the client wants
        filename = self.request.recv(1024).decode('utf-8')
        self.logger.debug('client asked for: %r', filename)

        # Send chunks of the file as they are compressed
        with open(filename, 'rb') as input:
            while True:
                block = input.read(BLOCK_SIZE)
                if not block:
                    break
                self.logger.debug('RAW %r', block)
                compressed = compressor.compress(block)
                if compressed:
                    self.logger.debug(
                        'SENDING %r',
                        binascii.hexlify(compressed))
                    self.request.send(compressed)
                else:
                    self.logger.debug('BUFFERING')

        # Send any data being buffered by the compressor
        remaining = compressor.flush()
        while remaining:
            to_send = remaining[:BLOCK_SIZE]
            remaining = remaining[BLOCK_SIZE:]
            self.logger.debug('FLUSHING %r',
                              binascii.hexlify(to_send))
            self.request.send(to_send)
        return


if __name__ == '__main__':
    import socket
    import threading
    from io import BytesIO

    logging.basicConfig(
        level=logging.DEBUG,
        format='%(name)s: %(message)s',
    )
    logger = logging.getLogger('Client')

    # Set up a server, running in a separate thread
    address = ('localhost', 0)  # let the kernel assign a port
    server = socketserver.TCPServer(address, ZlibRequestHandler)
    ip, port = server.server_address  # what port was assigned?

    t = threading.Thread(target=server.serve_forever)
    t.setDaemon(True)
    t.start()

    # Connect to the server as a client
    logger.info('Contacting server on %s:%s', ip, port)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))

    # Ask for a file
    requested_file = 'lorem.txt'
    logger.debug('sending filename: %r', requested_file)
    len_sent = s.send(requested_file.encode('utf-8'))

    # Receive a response
    buffer = BytesIO()
    decompressor = zlib.decompressobj()
    while True:
        response = s.recv(BLOCK_SIZE)
        if not response:
            break
        logger.debug('READ %r', binascii.hexlify(response))

        # Include any unconsumed data when
        # feeding the decompressor.
        to_decompress = decompressor.unconsumed_tail + response
        while to_decompress:
            decompressed = decompressor.decompress(to_decompress)
            if decompressed:
                logger.debug('DECOMPRESSED %r', decompressed)
                buffer.write(decompressed)
                # Look for unconsumed data due to buffer overflow
                to_decompress = decompressor.unconsumed_tail
            else:
                logger.debug('BUFFERING')
                to_decompress = None

    # deal with data reamining inside the decompressor buffer
    remainder = decompressor.flush()
    if remainder:
        logger.debug('FLUSHED %r', remainder)
        buffer.write(remainder)

    full_response = buffer.getvalue()
    lorem = open('lorem.txt', 'rb').read()
    logger.debug('response matches file contents: %s',
                 full_response == lorem)

    # Clean up
    s.close()
    server.socket.close()

我們人為的將這個代碼清單做了一些劃分,以展示緩衝行為,如果將數據傳遞到compress()或decompress(),但沒有得到完整的壓縮或未壓縮輸出塊,此時便會進行緩衝。

客戶連接到套接字,並請求一個文件。然後迴圈,接收壓縮數據塊。由於一個塊可能未包含足夠多的信息來完全解壓縮,所以之前接收的剩餘數據將與新數據結合,並且傳遞到解壓縮器。解壓縮數據時,會把它追加到一個緩衝區,處理迴圈結束時將與文件內容進行比較。


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

-Advertisement-
Play Games
更多相關文章
  • 目錄 . 一、基本概念 . 1、背景 . 2、簡介 . 3、特點 . 4、基礎模型 . 5、Apollo 的四個維度 . 6、本地緩存 . 7、客戶端設計 . 8、總體設計 . 9、可用性考慮 . 二、Apollo 配置中心創建項目與配置 . 1、登錄 Apollo . 2、修改與增加部門數據 . ...
  • 實現步驟 1.導包:import java.util.Scanner; 2.Scanner類的實例化:Scanner scan = new Scanner(System.in); 3.調用Scanner類的相關方法(next() nextInt())獲取指定類型的變數; 註意:在控制台,如果輸入的類 ...
  • 一、分類 順序結構:程式從上而下依次執行 分支結構:if-else if-else、switch-case 迴圈結構:while迴圈、for迴圈、do-while迴圈、增強for迴圈 二、具體說明 1.分支結構 1.1if分支結構 說明 1.else結構是可選的; 2.if-else結構可以嵌套使用 ...
  • 原文鏈接:http://www.yiidian.com/servlet/servlet how work.html 接下來我們有必要瞭解下Servlet的工作原理,這樣才能更好地理解Servlet。本文我們將以之前開發過的Servlet程式來講解Servlet的內部細節。 1 Servlet基本執行 ...
  • 1. 方法 註:class(類)是具有相同的屬性和方法的對象的集合。 2. 例子 (1)數據/集合類型 str(object=''); str(object=b'', encoding='utf-8', errors='strict') int(x, base=10) float(x=0) comp ...
  • 我用java爬蟲爬了一個圖片網站 最近想建立個網站,不想搞技術博客之類的網站了,因為像博客園還有CSDN這種足夠了。平時的問題也都是這些記錄一下就夠了。那搞個什麼網站好玩呢? 看到一個圖片網站還不錯,裡面好多圖片(當然有xxx圖片了....)哈哈,其實就是閑的,同時也介紹一下java爬蟲的相關用法把 ...
  • 目錄 一級緩存 二級緩存 自定義緩存 一級緩存 MyBatis 包含了一個非常強大的查詢緩存特性,它可以非常方便地配置和定製。MyBatis 3 中的緩存實現的很多改進都已經實現了,使得它更加強大而且易於配置。mybatis預設情況下只會開啟一級緩存,也就是局部的 session 會話緩存。 首先我 ...
  • 區別 1.使用範圍和規範不同 filter是servlet規範規定的,只能用在web程式中. 攔截器即可以用在web程式中, 也可以用於application, swing程式中, 是Spring容器內的, 是Spring框架支持的 2.觸發時機不同 順序: Filter Servlet Inter ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...