Domanda

Sto chiamando un'API divertente che restituisce un array di byte, ma voglio un flusso di testo. C'è un modo semplice per ottenere un flusso di testo da un array di byte? Per ora ho appena messo insieme:

(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))

e quindi avvolgi il risultato con input-from-string, ma non può essere il modo migliore. (Inoltre, è terribilmente inefficiente.)

In questo caso, so che è sempre ASCII, quindi interpretarlo come ASCII o UTF-8 andrebbe bene. Sto usando SBCL compatibile con Unicode, ma preferirei una soluzione portatile (anche solo ASCII) a una specifica SBCL-Unicode.

È stato utile?

Soluzione

FLEXI-STREAMS ( http://weitz.de/flexi-streams/ ) è portatile funzione di conversione

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

=>

"Hello"

Oppure, se vuoi uno stream:

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

restituirà uno stream che legge il testo dal byte-vector

Altri suggerimenti

Esistono due librerie portatili per questa conversione:

  • flexi-stream, già menzionato in un'altra risposta.

    Questa libreria è più vecchia e ha più funzionalità, in particolare i flussi estensibili.

  • Babel , una libreria specifica per la codifica e la decodifica dei caratteri

    Il vantaggio principale di Babel rispetto ai flussi flessibili è la velocità.

Per prestazioni ottimali, usa Babel se ha le caratteristiche di cui hai bisogno, e ricorrere ai flussi flessibili altrimenti. Sotto un microbenchmark (leggermente poco scientifico) che illustra la differenza di velocità.

Per questo caso di test, Babel è 337 volte più veloce e ha bisogno di 200 volte meno 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.
|#

Se non devi preoccuparti della codifica UTF-8 (che, in sostanza, significa "solo ASCII"), potresti essere in grado di usare MAP:

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

Dico di andare con le proposte di flexistream o babel proposte.

Ma solo per completezza e beneficio dei futuri googler che arrivano su questa pagina, voglio menzionare lo sb-ext: octets-to-string di 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 supporta i cosiddetti Grey Streams . Si tratta di flussi estensibili basati su classi CLOS e funzioni generiche. È possibile creare una sottoclasse del flusso di testo che ottiene i caratteri dall'array di byte.

Prova la funzione FORMAT . (FORMAT NIL ...) restituisce i risultati come stringa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top