2009-03-01 15 views
19

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 buttato insieme:Come convertire l'array di byte in stringa in Common Lisp?

(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 poi avvolgere il risultato con-input-da-string, ma che non può essere il modo migliore. (Inoltre, è orribilmente inefficiente.)

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

risposta

27

Flexi-flussi (http://weitz.de/flexi-streams/) ha funzione di conversione portatile

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

=> 

"Hello" 

Oppure, se si desidera un flusso:

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

restituirà un ruscello che legge il testo da byte-vector

0

Provare la funzione FORMAT. (FORMAT NIL ...) restituisce i risultati sotto forma di stringa.

+0

Vero, ma sarebbe ancora facendo un stringa intermedia. Spero in una soluzione che possa avvolgere l'array di byte esistente in uno stream senza O (n) più spazio di archiviazione. Immagino che il mio titolo non sia stato molto buono. :-) – Ken

4

SBCL supporta il cosiddetto Gray Streams. Questi sono stream estensibili basati su classi CLOS e funzioni generiche. È possibile creare una sottoclasse di flusso di testo che ottiene i caratteri dall'array di byte.

23

Esistono due librerie portatili per questa conversione:

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

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

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

    Il vantaggio principale di Babele over flexi-stream è la velocità.

Per ottenere prestazioni ottimali, utilizzare Babel se ha le caratteristiche necessarie, e ricadere a Flexi-flussi altrimenti. Sotto un microbenchmark (leggermente non scientifico) che illustra la differenza di velocità.

Per questo test case, 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. 
|# 
14

Se non devi preoccuparti di codifica UTF-8 (che, in sostanza, significa "solo ASCII"), si può essere in grado di utilizzare MAP:

(map 'stringa # 'code-char # (72 101 108 108 111))
+2

Bella soluzione che funziona con Common Lisp immediato :) – Olie

+0

A seconda del supporto UTF-8 del sistema Lisp, questo potrebbe funzionare anche per UTF-8. Ad esempio, sbcl e ccl supportano utf-8 ora. La portabilità è un'altra questione ... – vancan1ty

+2

@ vancan1ty No, questo non funziona con UTF-8. Ad esempio, '206 177' codifica' "α" 'in UTF-8, ma' (princ (mappa 'stringa #' code-char # (206 177))) 'restituisce' "α" 'in SBCL. –

4

Dico di andare con le soluzioni flexistream o babel proposte.

Ma solo per la completezza e il beneficio di Googler futuri che arrivano a questa pagina voglio citare proprio sb-ext di SBCL: ottetti a stringa:

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: 
    * 
+0

"beneficio dei futuri googler": futuro beneficiario qui, otto anni dopo. (duckduckgo, ma hey) –