ファイルからの入力

ファイルからの入力の仕方。

 cf. 9. 入出力 – もうひとつの Scheme 入門

より。

open-input-file、read-char、eof-object?

open-input-file はファイルを開いてファイルからの入力ポートを返す。read-char はポートから1文字読み込む。ファイルの終わりに達すると、eof-object を返すので、eof-ofject? でチェックする。

次の例は、sample.txt からファイルの内容を読み込む read-file 関数。

(define read-file
  (lambda (file-name)
    (let ((p (open-input-file file-name)))
      (let loop ((ls '()) (c (read-char p)))
        (if (eof-object? c)
            (begin
              (close-input-port p)
              (list->string (reverse ls)))
            (loop (cons c ls) (read-char p)))))))

3行目で入力ポートを開いて変数 p を束縛している。4行目で p から1文字読み込んで、5行目で eof-object 可動化のチェック。もしファイル終端ならポートを閉じて(7行目)文字列を返し、そうでないなら再帰(9行目)している。
(begin ...) というのは、複数のS式を順に評価して最後のS式の値を返すものらしい。

実行例:

^o^ > type sample.txt
Hello world!
Scheme is an elegant programming language.

^o^ > gosh -I.
gosh> (load "read-file1.scm")
#t
gosh> (read-file "sample.txt")
"Hello world!\r\nScheme is an elegant programming language.\r\n"

call-with-input-file

(call-with-input-file filename procedure) というかたちをしていて、procedure は入力ポートを引数にとる関数。エラー処理をしてくれるのでこちらのほうが便利、と書いてある。
call-with-input-file を使って read-file 関数を書くと次にようになる。

(define read-file
  (lambda (file-name)
    (call-with-input-file file-name
      (lambda (p)
        (let loop ((ls '()) (c (read-char p)))
          (if (eof-object? c)
              (begin
                (close-input-port p)
                (list->string (reverse ls)))
              (loop (cons c ls) (read-char p))))))))

ファイルから文字を読み込んで返すという本質的なところは read-file1.scm とおなじ。違うのは、let で入力ポートを束縛する代わりに、ポートを引数に取る関数を使っているところ。

実行例:

gosh> (load "read-file2.scm")
#t
gosh> (read-file "sample.txt")
"Hello world!\r\nScheme is an elegant programming language.\r\n"

with-input-from-file

with-input-from-file は、ファイルを標準入力として開く。call-with-input-file と同じように (with-input-from-file filename procedure) というかたちをしているけど、procedure は引数をとらない。つまり入力は標準入力に固定されるってわけだな。入力ポートは勝手に閉じられる。

(define read-file
  (lambda (file-name)
    (with-input-from-file file-name
      (lambda ()
        (let loop ((ls '()) (c (read-char)))
          (if (eof-object? c)
              (list->string (reverse ls))
              (loop (cons c ls) (read-char))))))))

実行例:

gosh> (load "read-file3.scm")
#t
gosh> (read-file "sample.txt")
"Hello world!\r\nScheme is an elegant programming language.\r\n"

これが一番使いやすそうだ。

[追記]
最後の例では read-char を引数なしで使っている。read-char 関数は引数がなければ標準入力から読み込むってことでいいのかな。