peek-char
の便利な機能
ストリームから文字を peek する際,peek-char
の第 1 引数に t
を指定すると空白文字をスキップしてくれる.次に read-char
を実行すると,スキップ後の 1 文字を返す.
(with-input-from-string (in " 1 2 3 4 5")
(format t "Peek: ~s Read: ~s~%" (peek-char t in) (read-char in)) ; (1)
(format t "Peek: ~s Read: ~s~%" (peek-char nil in) (read-char in)) ; (2)
(format t "Peek: ~s Read: ~s~%" (peek-char nil in) (read-char in))) ; (3)
;=> Peek: #\1 Read: #\1
; Peek: #\ Read: #\
; Peek: #\2 Read: #\2
1 | peek-char の第 1 引数は t なので先頭の空白文字はスキップされ #\1 が返る.read-char は直前の peek-char が返したスキップ後の #\1 を返す. |
2 | peek-char の第 1 引数は nil なので入力文字列の 1 と 2 の間のスペース文字が返される. |
3 | 3 行目の read-char でストリームのポインタが 1 つ前進し入力文字列の 2 を指しているので 4 行目の peek-char の第 1 引数が nil でも #\2 が返る. |
ストリームから文字列を作成するときのバッファリング
字句解析などでストリームからキャラクタを読み込む際,読み込む文字列の長さが未確定の場合は伸縮可能な CHARACTER
型の配列に vector-push-extend
で文字をプッシュしていく.その際,配列が全て文字列で埋まるように長さ 0 の配列を用意し,そこに文字をプッシュする必要はない.Lisp は fill-pointer
で管理している長さまでしか文字列として認識しないので,初期化した配列より短い文字列でも問題ない.したがって,用意する配列は,バッファとして十分な大きさを用意すれば良い.
以下のコードはバファとして長さ 100 の配列を用意しているが,文字列を表示させると読み込んだ分しか表示されていない.また,同じ 3 文字から成る別の文字列と比較しても,等しい文字列として認識されているのがわかる.
(let ((str (make-array 100 ; 初期バッファサイズ
:element-type 'character
:fill-pointer 0
:adjustable t)))
(with-input-from-string (in "今日は晴れ.明日は雨.")
(dotimes (i 3) ; 最初の3文字のみ
(vector-push-extend (read-char in) str))) ; バッファに読み込む
(format t "~a" str)
(string= "今日は" str))
;=> 今日は
; T
一方,fill-pointer
の無い simple-string
をバッファに使用した以下の例では,バッファの大きさ全てが文字列として扱われることになる.
(let ((str (make-string 10))) ; 初期バッファサイズ
(with-input-from-string (in "今日は晴れ.明日は雨.")
(dotimes (i 3) ; 最初の3文字のみ
(setf (schar str i) (read-char in)))) ; バッファに読み込む
(format t "~a" str)
(string= "今日は" str))
;=> 今日は^@^@^@^@^@^@^@
NIL