Common Lispでバイト配列を文字列に変換する方法は?
-
03-07-2019 - |
質問
バイト配列を返す面白いAPIを呼び出していますが、テキストストリームが必要です。バイト配列からテキストストリームを取得する簡単な方法はありますか?今のところ、私は一緒に投げました:
(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))
そして結果をwith-input-from-stringでラップしますが、それは最良の方法ではありません。 (さらに、ひどく非効率的です。)
この場合、常にASCIIであることがわかっているため、ASCIIまたはUTF-8として解釈しても問題ありません。ユニコード対応のSBCLを使用していますが、SBCLユニコード固有のソリューションよりも、ポータブルな(ASCIIのみの)ソリューションを好むでしょう。
解決
FLEXI-STREAMS( http://weitz.de/flexi-streams/ )はポータブルです変換関数
(flexi-streams:octets-to-string #(72 101 108 108 111) :external-format :utf-8)
=>
"Hello"
または、ストリームが必要な場合:
(flexi-streams:make-flexi-stream
(flexi-streams:make-in-memory-input-stream
#(72 101 108 108 111))
:external-format :utf-8)
バイトベクトルからテキストを読み取るストリームを返します
他のヒント
この変換には2つのポータブルライブラリがあります:
-
flexi-streams、別の回答ですでに言及されています。
このライブラリは古く、より多くの機能、特に拡張可能なストリームがあります。
-
Babel 、文字エンコードおよびデコード専用のライブラリ
フレキシストリームに対するBabelの主な利点は速度です。
最高のパフォーマンスを得るには、必要な機能がある場合はBabelを使用し、それ以外の場合はflexi-streamsにフォールバックします。速度差を示す(わずかに非科学的な)マイクロベンチマークの下。
このテストケースの場合、Babelは 337倍高速であり、必要なメモリは200倍少なくなります。
(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.
|#
UTF-8エンコーディング(本質的には「単なるASCII」を意味する)を心配する必要がない場合は、MAPを使用できます:
(map 'string#' code-char#(72 101108108111))
提案されているflexistreamまたはbabelソリューションを使用すると言います。
しかし、完全性とこのページに到着する将来のグーグルの利益のために、私はsbcl自身のsb-ext:octets-to-string:に言及したいです:
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は、いわゆるグレーストリームをサポートしています。これらは、CLOSクラスと汎用関数に基づいた拡張可能なストリームです。バイト配列から文字を取得するテキストストリームサブクラスを作成できます。
FORMAT
関数を試してください。 (FORMAT NIL ...)
は、結果を文字列として返します。