Question

J'appelle une API amusante qui renvoie un tableau d'octets, mais je veux un flux de texte. Existe-t-il un moyen simple d’obtenir un flux de texte à partir d’un tableau d’octets? Pour l'instant je viens de lancer ensemble:

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

puis encapsulez le résultat dans with-input-from-string, mais cela ne peut pas être le meilleur moyen. (En plus, c'est horriblement inefficace.)

Dans ce cas, je sais qu’il s’agit toujours d’ASCII. Il serait donc bon de l’interpréter comme ASCII ou UTF-8. J'utilise SBCL compatible Unicode, mais je préférerais une solution portable (même ASCII uniquement) à une solution spécifique à SBCL-Unicode.

Était-ce utile?

La solution

FLEXI-STREAMS ( http://weitz.de/flexi-streams/ ) est portable fonction de conversion

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

=>

"Hello"

Ou, si vous voulez un flux:

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

retournera un flux qui lit le texte à partir d'un vecteur octet

Autres conseils

Il existe deux bibliothèques portables pour cette conversion:

  • flexi-streams, déjà mentionnés dans une autre réponse.

    Cette bibliothèque est plus ancienne et possède plus de fonctionnalités, en particulier les flux extensibles.

  • Babel , une bibliothèque spécifique pour le codage et le décodage de caractères

    Le principal avantage de Babel par rapport aux flux flexibles est la rapidité.

Pour des performances optimales, utilisez Babel si ce dernier offre les fonctionnalités dont vous avez besoin et utilisez sinon le mode flexi-stream. Ci-dessous un micro-repère (peu scientifique) illustrant la différence de vitesse.

Pour ce cas de test, Babel est 337 fois plus rapide et nécessite 200 fois moins de mémoire.

(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 vous n'avez pas à vous soucier de l'encodage UTF-8 (qui signifie essentiellement "tout simplement ASCII"), vous pourrez peut-être utiliser MAP:

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

Je vous invite à choisir les solutions flexistream ou babel proposées.

Mais juste pour être complet et pour le bénéfice des futurs googlers arrivant sur cette page, je voudrais mentionner le 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 prend en charge les Gray Streams . Ce sont des flux extensibles basés sur des classes CLOS et des fonctions génériques. Vous pouvez créer une sous-classe de flux de texte qui récupère les caractères du tableau d'octets.

Essayez la fonction FORMAT . (FORMAT NIL ...) renvoie les résultats sous forme de chaîne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top