在chipz decompression library中有一个非常有用的函数make-decompressing-stream
,它提供了一个接口(在幕后使用Gray流)来透明地解压缩从提供的流中读取的数据。这允许我编写单个函数read-tag
(从结构化二进制数据流中读取单个"标记"就像Common Lisp' read
函数读取a单个Lisp"形式"来自流),适用于压缩和未压缩数据,例如:
;; For uncompressed data:
(read-tag in-stream)
;; For compressed data:
(read-tag (chipz:make-decompressing-stream 'chipz:zlib in-stream))
据我所知,关联的压缩库的API salza2并没有为(开箱即用的)等效接口提供执行相反的任务。我怎么能自己实现这样的界面?我们称之为make-compressing-stream
。它将与我自己的补充write-tag
函数一起使用,并提供与阅读相同的好处:
;; For uncompressed-data:
(write-tag out-stream current-tag)
;; For compressed data:
(write-tag (make-compressing-stream 'salza2:zlib-compressor out-stream)
current-tag)
在salza2的文档中(上面链接),在概述中,它说:" Salza2提供了一个用于创建压缩器对象的界面。该对象充当八位字节(单个或八位字节的向量)的接收器,并且是压缩数据格式的八位字节的源。压缩的八位字节数据被提供给用户定义的回调,该回调可以将其写入流,将其复制到另一个向量等等。就我目前的目的而言,我只需要以zlib和gzip格式进行压缩,为此提供标准压缩器。
所以,我认为可以这样做:首先,转换我的"标签"对象为八位字节向量,其次使用salza2:compress-octet-vector
对其进行压缩,第三,提供将压缩数据直接写入文件的回调函数。通过阅读,我认为可以使用flexi-streams:with-output-to-sequence
实现第一步 - 请参阅here - 但我真的不确定回调函数,尽管我们看过salza2的来源。但事情就是这样:单个标签可以包含任意数量的任意嵌套标签,以及" leaf"这种结构的标签每个都可以携带相当大的有效载荷;换句话说,单个标签可以包含大量数据。
因此,标签 - > uncompressed-octets->压缩八位字节 - >文件转换理想情况下需要以块的形式执行,这就提出了一个我不知道如何回答的问题,即:压缩格式 - AIUI - 倾向于在其标题中存储其有效载荷数据的校验和;如果我一次压缩一个数据块并将每个压缩的块附加到输出文件,肯定会有每个块的头和校验和,而不是单个头和整个标记数据的校验和,这是我想要的?我怎么解决这个问题?或者它已经由salza2处理了?
感谢您的帮助,抱歉漫无边际:)
答案 0 :(得分:2)
据我了解,您不能直接从单个文件解压缩多个块。
(defun bytes (&rest elements)
(make-array (length elements)
:element-type '(unsigned-byte 8)
:initial-contents elements))
(defun compress (chunk &optional mode)
(with-open-file (output #P"/tmp/compressed"
:direction :output
:if-exists mode
:if-does-not-exist :create
:element-type '(unsigned-byte 8))
(salza2:with-compressor (c 'salza2:gzip-compressor
:callback (salza2:make-stream-output-callback output))
(salza2:compress-octet-vector chunk c))))
(compress (bytes 10 20 30) :supersede)
(compress (bytes 40 50 60) :append)
现在,/tmp/compressed
包含两个连续的压缩数据块。
调用decompress
只读取第一个块:
(chipz:decompress nil 'chipz:gzip #P"/tmp/compressed")
=> #(10 20 30)
查看chipz
的源代码,使用内部缓冲区读取流,这意味着第一个块之后的字节可能已经读取但未解压缩。这就解释了为什么当在同一个流上使用两个连续decompress
个调用时,第二个调用EOF会出错。
(with-open-file (input #P"/tmp/compressed"
:element-type '(unsigned-byte 8))
(list
#1=(multiple-value-list(ignore-errors(chipz:decompress nil 'chipz:gzip input)))
#1#))
=> ((#(10 20 30))
(NIL #<CHIPZ:PREMATURE-END-OF-STREAM {10155E2163}>))
我不知道数据应该有多大,但如果它成为问题,您可能需要更改解压缩算法,以便在我们处于done
状态时(请参阅膨胀。 lisp),返回足够的数据以将剩余的字节作为新块处理。或者,您压缩到不同的文件并使用像TAR这样的存档格式(参见https://github.com/froydnj/archive)。