Reputation: 71
I need to produce a string of bytes from a list of integers (in order to send them to a C program via a socket connection). I'm trying to replicate some Python code that uses the 'struct' module. The Python code below produces this output:
import struct
struct.pack('HHHH', 1, 2, 3, 4)
-->
'\x01\x00\x02\x00\x03\x00\x04\x00'
The equivalent Lisp code produces the following output:
(ql:quickload :pack)
(pack:pack "HHHH" 1 2 3 4)
-->
#(1 0 2 0 3 0 4 0)
How can I convert this array to a string format with the same representations as produced by the Python code?
Many thanks,
David
Upvotes: 2
Views: 1321
Reputation: 3885
When you say "string of bytes", the statement is imprecise. In CL, strings are vectors of characters. A character does not have a specified size. Most Common Lisp implementations today use plain Unicode codepoints for characters, meaning that each character is at least 21 bits (but usually 32) in size.
What you probably want is a vector of 8-bit bytes. These have a Lisp type of (VECTOR (UNSIGNED-BYTE 8) *)
.
Thus, the easiest way to convert your list of numbers into such a vector is by using:
(coerce '(1 2 3 4) '(vector (unsigned-byte 8)))
However, it is likely that you don't actually need to do this, since this is taken care of automatically as long as the stream's element type is set to the correct type. I don't know what API you are using to open your network connection, but if you're using usocket, the correct form is:
(usocket:socket-connect host port :element-type '(unsigned-byte 8))
You can then write your list of numbers directly to the stream using WRITE-SEQUENCE
, and they will be sent as bytes just as you'd expect.
Finally, I'd like to explain why your string trick may have worked. What likely happened is that you opened your stream as a character stream, which causes your string to be written using some specific encoding. Most string encodings are ASCII-compatible (for example, UTF-8) meaning that any byte in the range 0-127 will be sent as that byte, while values above 127 will likely result in all sorts of unpredictable values (unless your encoding is ISO 8859-1 in which case values up to 255 will also be correct). Relying on string encodings to send binary data is a really bad idea, and I'd recommend you use a proper binary stream when sending binary data.
Upvotes: 4
Reputation: 27424
A very simple way of transforming a byte array into a string is through the use of the map
predefined function:
(map 'string #'code-char (pack:pack "HHHH" 1 2 3 4))
Upvotes: 2