Pregunta

Estoy llamando a una API divertida que devuelve una matriz de bytes, pero quiero una secuencia de texto. ¿Hay una manera fácil de obtener un flujo de texto de una matriz de bytes? Por ahora acabo de tirar juntos:

(defun bytearray-to-string (bytes)
  (let ((str (make-string (length bytes))))
    (loop for byte across bytes
       for i from 0
       do (setf (aref str i) (code-char byte)))
    str))

y luego envuelve el resultado en with-input-from-string, pero esa no puede ser la mejor manera. (Además, es horriblemente ineficiente).

En este caso, sé que siempre es ASCII, por lo que interpretarlo como ASCII o UTF-8 estaría bien. Estoy utilizando SBCL para Unicode, pero preferiría una solución portátil (incluso solo ASCII) a una específica para SBCL-Unicode.

¿Fue útil?

Solución

FLEXI-STREAMS ( http://weitz.de/flexi-streams/ ) tiene portátil función de conversión

(flexi-streams:octets-to-string #(72 101 108 108 111) :external-format :utf-8)

=>

"Hello"

O, si quieres una transmisión:

(flexi-streams:make-flexi-stream
   (flexi-streams:make-in-memory-input-stream
      #(72 101 108 108 111))
   :external-format :utf-8)

devolverá una secuencia que lee el texto de byte-vector

Otros consejos

Hay dos bibliotecas portátiles para esta conversión:

  • flexi-streams, ya mencionados en otra respuesta.

    Esta biblioteca es más antigua y tiene más funciones, en particular las secuencias extensibles.

  • Babel , una biblioteca específica para la codificación y decodificación de caracteres

    La principal ventaja de Babel sobre los flujos flexibles es la velocidad.

Para obtener el mejor rendimiento, use Babel si tiene las funciones que necesita y, de lo contrario, vuelva a los flujos flexibles. Debajo de un microbenchmark (poco científico) que ilustra la diferencia de velocidad.

Para este caso de prueba, Babel es 337 veces más rápido y necesita 200 veces menos memoria.

(asdf:operate 'asdf:load-op :flexi-streams)
(asdf:operate 'asdf:load-op :babel)

(defun flexi-streams-test (bytes n)
  (loop
     repeat n
     collect (flexi-streams:octets-to-string bytes :external-format :utf-8)))

(defun babel-test (bytes n)
  (loop
     repeat n
     collect (babel:octets-to-string bytes :encoding :utf-8)))

(defun test (&optional (data #(72 101 108 108 111))
                       (n 10000))
  (let* ((ub8-vector (coerce data '(simple-array (unsigned-byte 8) (*))))
         (result1 (time (flexi-streams-test ub8-vector n)))
         (result2 (time (babel-test ub8-vector n))))
    (assert (equal result1 result2))))

#|
CL-USER> (test)
Evaluation took:
  1.348 seconds of real time
  1.328083 seconds of user run time
  0.020002 seconds of system run time
  [Run times include 0.12 seconds GC run time.]
  0 calls to %EVAL
  0 page faults and
  126,402,160 bytes consed.
Evaluation took:
  0.004 seconds of real time
  0.004 seconds of user run time
  0.0 seconds of system run time
  0 calls to %EVAL
  0 page faults and
  635,232 bytes consed.
|#

Si no tiene que preocuparse por la codificación UTF-8 (que, en esencia, significa "simplemente ASCII"), puede usar MAP:

(map 'string #' code-char # (72 101 108 108 111))

Yo digo que vayan con las soluciones propuestas de flexistream o babel.

Pero solo para estar completo y para el beneficio de los futuros googlers que llegan a esta página, quiero mencionar el sb-ext: octets-to-string de sbcl:

   SB-EXT:OCTETS-TO-STRING is an external symbol in #<PACKAGE "SB-EXT">.
   Function: #<FUNCTION SB-EXT:OCTETS-TO-STRING>
   Its associated name (as in FUNCTION-LAMBDA-EXPRESSION) is
     SB-EXT:OCTETS-TO-STRING.
   The function's arguments are:  (VECTOR &KEY (EXTERNAL-FORMAT DEFAULT) (START 0)
                                          END)
   Its defined argument types are:
     ((VECTOR (UNSIGNED-BYTE 8)) &KEY (:EXTERNAL-FORMAT T) (:START T) (:END T))
   Its result type is:
     *

SBCL admite los llamados Gray Streams . Estas son secuencias extensibles basadas en clases CLOS y funciones genéricas. Podría crear una subclase de secuencia de texto que obtenga los caracteres de la matriz de bytes.

Prueba la función FORMAT . (FORMAT NIL ...) devuelve los resultados como una cadena.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top