如何在 Common Lisp 中将二维字节数组写入二进制文件?
How to write a 2D byte array to a binary file in Common Lisp?
我想这对于有 Common Lisp 经验的人来说是个简单的问题。对刚开始使用 LISP 的我来说不是那么多。
正如您在下面的下一个片段中看到的,我创建了一个 800 x 600 类型的 UNSIGNED BYTE
数组。
(defun test-binary-save ()
(let*
((width 800)
(height 600)
(arr (make-array (list width height)
:element-type '(mod 256)
:initial-element 0)))
(utilities::save-array-as-pgm "test.pgm" arr)))
我的实用程序包中的函数应该以 P5 PGM
格式写入磁盘。
(defun save-array-as-pgm (filename buffer)
"Writes a byte array as a PGM file (P5) to a file."
(with-open-file
(stream filename
:element-type '(unsigned-byte 8)
:direction :output
:if-does-not-exist :create
:if-exists :supersede)
(let*
((dimensions (array-dimensions buffer))
(width (first dimensions))
(height (second dimensions))
(header (format nil "P5~A~D ~D~A255~A"
#\newline
width height #\newline
#\newline)))
(loop
:for char :across header
:do (write-byte (char-code char) stream))
;(write-sequence buffer stream) <<-- DOES NOT WORK - is not of type SEQUENCE
))
filename)
做同样事情的等效(和工作)C 函数如下所示。
static
int
save_pgm
( const char* filename
, size_t width
, size_t height
, const uint8_t* pixels
)
{
if(NULL == filename)
return 0;
if(NULL == pixels)
return 0;
FILE *out = fopen(filename, "wb");
if(NULL != out)
{
fprintf(out, "P5\n%zu %zu\n255\n", width, height);
size_t nbytes = width * height;
fwrite(pixels,1,nbytes,out);
fclose(out);
return 1;
}
return 0;
}
谁能告诉我如何修复我的 save-array-as-pgm
函数,最好是一次写入数组,而不是使用循环和 (write-byte (aref buffer y x) stream)
?
在我决定在这里问这个问题之前,我在谷歌上搜索了很多,只找到了对一些做花哨的二进制内容的包的引用——但这是一个简单的案例,我正在寻找一个简单的解决方案。
如果你想在 Common Lisp 中做真正的位推,使用一维数组。
看起来这毕竟不是那么困难...一旦我发现可以将 2D 数组 "cast" 转换为 1D 数组然后简单地使用 write-sequence
.
为了找到解决方案,我必须检查 github 上的 sbcl 源代码以了解 make-array
的工作原理并找到 - sbcl - 特定函数 array-storage-vector
。
如我所料,多维数组使用一维后备数组进行数据存储。
函数 save-array-as-pgm
现在看起来像这样:
(defun save-array-as-pgm (filename buffer)
"Writes a byte array as a PGM file (P5) to a file."
(with-open-file
(stream filename
:element-type '(unsigned-byte 8)
:direction :output
:if-does-not-exist :create
:if-exists :supersede)
(let*
((dimensions (array-dimensions buffer))
(width (first dimensions))
(height (second dimensions))
(header (format nil "P5~A~D ~D~A255~A"
#\newline
width height #\newline
#\newline)))
(loop
:for char :across header
:do (write-byte (char-code char) stream))
(write-sequence (sb-c::array-storage-vector buffer) stream)
))
filename)
Common Lisp 支持 displaced 数组:
CL-USER 6 > (let ((array (make-array (list 3 4)
:initial-element 1
:element-type 'bit)))
(make-array (reduce #'* (array-dimensions array))
:element-type 'bit
:displaced-to array))
#*111111111111
置换数组本身没有存储空间,而是使用另一个数组的存储空间。它可以有不同的维度。
现在的问题是 Lisp 实现如何有效地通过置换数组访问数组。
我想这对于有 Common Lisp 经验的人来说是个简单的问题。对刚开始使用 LISP 的我来说不是那么多。
正如您在下面的下一个片段中看到的,我创建了一个 800 x 600 类型的 UNSIGNED BYTE
数组。
(defun test-binary-save ()
(let*
((width 800)
(height 600)
(arr (make-array (list width height)
:element-type '(mod 256)
:initial-element 0)))
(utilities::save-array-as-pgm "test.pgm" arr)))
我的实用程序包中的函数应该以 P5 PGM
格式写入磁盘。
(defun save-array-as-pgm (filename buffer)
"Writes a byte array as a PGM file (P5) to a file."
(with-open-file
(stream filename
:element-type '(unsigned-byte 8)
:direction :output
:if-does-not-exist :create
:if-exists :supersede)
(let*
((dimensions (array-dimensions buffer))
(width (first dimensions))
(height (second dimensions))
(header (format nil "P5~A~D ~D~A255~A"
#\newline
width height #\newline
#\newline)))
(loop
:for char :across header
:do (write-byte (char-code char) stream))
;(write-sequence buffer stream) <<-- DOES NOT WORK - is not of type SEQUENCE
))
filename)
做同样事情的等效(和工作)C 函数如下所示。
static
int
save_pgm
( const char* filename
, size_t width
, size_t height
, const uint8_t* pixels
)
{
if(NULL == filename)
return 0;
if(NULL == pixels)
return 0;
FILE *out = fopen(filename, "wb");
if(NULL != out)
{
fprintf(out, "P5\n%zu %zu\n255\n", width, height);
size_t nbytes = width * height;
fwrite(pixels,1,nbytes,out);
fclose(out);
return 1;
}
return 0;
}
谁能告诉我如何修复我的 save-array-as-pgm
函数,最好是一次写入数组,而不是使用循环和 (write-byte (aref buffer y x) stream)
?
在我决定在这里问这个问题之前,我在谷歌上搜索了很多,只找到了对一些做花哨的二进制内容的包的引用——但这是一个简单的案例,我正在寻找一个简单的解决方案。
如果你想在 Common Lisp 中做真正的位推,使用一维数组。
看起来这毕竟不是那么困难...一旦我发现可以将 2D 数组 "cast" 转换为 1D 数组然后简单地使用 write-sequence
.
为了找到解决方案,我必须检查 github 上的 sbcl 源代码以了解 make-array
的工作原理并找到 - sbcl - 特定函数 array-storage-vector
。
如我所料,多维数组使用一维后备数组进行数据存储。
函数 save-array-as-pgm
现在看起来像这样:
(defun save-array-as-pgm (filename buffer)
"Writes a byte array as a PGM file (P5) to a file."
(with-open-file
(stream filename
:element-type '(unsigned-byte 8)
:direction :output
:if-does-not-exist :create
:if-exists :supersede)
(let*
((dimensions (array-dimensions buffer))
(width (first dimensions))
(height (second dimensions))
(header (format nil "P5~A~D ~D~A255~A"
#\newline
width height #\newline
#\newline)))
(loop
:for char :across header
:do (write-byte (char-code char) stream))
(write-sequence (sb-c::array-storage-vector buffer) stream)
))
filename)
Common Lisp 支持 displaced 数组:
CL-USER 6 > (let ((array (make-array (list 3 4)
:initial-element 1
:element-type 'bit)))
(make-array (reduce #'* (array-dimensions array))
:element-type 'bit
:displaced-to array))
#*111111111111
置换数组本身没有存储空间,而是使用另一个数组的存储空间。它可以有不同的维度。
现在的问题是 Lisp 实现如何有效地通过置换数组访问数组。