zoukankan      html  css  js  c++  java
  • Python3标准库:zlib GNUzlib压缩

    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.
    ' * 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(),但没有得到完整的压缩或未压缩输出块,此时便会进行缓冲。

    客户连接到套接字,并请求一个文件。然后循环,接收压缩数据块。由于一个块可能未包含足够多的信息来完全解压缩,所以之前接收的剩余数据将与新数据结合,并且传递到解压缩器。解压缩数据时,会把它追加到一个缓冲区,处理循环结束时将与文件内容进行比较。

  • 相关阅读:
    Openstack API 开发 快速入门
    virtualBox虚拟机到vmware虚拟机转换
    使用Blogilo 发布博客到cnblogs
    Openstack Troubleshooting
    hdoj 1051 Wooden Sticks(上升子序列个数问题)
    sdut 2430 pillars (dp)
    hdoj 1058 Humble Numbers(dp)
    uva 10815 Andy's First Dictionary(快排、字符串)
    sdut 2317 Homogeneous squares
    hdoj 1025 Constructing Roads In JGShining's Kingdom(最长上升子序列+二分)
  • 原文地址:https://www.cnblogs.com/liuhui0308/p/12583668.html
Copyright © 2011-2022 走看看