ファイル読み込み
Scheme で指定したファイルを読み込む方法を考えてみた。
(define (readfile filepath) (let ((port (open-input-file filepath))) (let loop ((contents "") (c (read-char port))) (if (eof-object? c) (begin (close-input-port port) contents) (loop (string-append contents (make-string 1 c)) (read-char port))))))
これでよいのだろうか、という気がしないでもないのは、string-append は元の文字列に追加するのではなく、新しい領域を割り当てて追加した文字列を返す点と、追加する文字列を新規に作らなければならない点。
Java だとこんな感じになる。
public String readfile(String filepath) throws Exception { String contents = ""; FileInputStream fis = new FileInputStream(filepath); int b; while ((b = fis.read()) != -1) { contents += new String(new char[] { (char)b }); } fis.close(); return contents; }
かなり無駄が多い。
Scheme は一時オブジェクトをリストにしたほうがよいのかも、と思って以下のようにしてみた。
(define (readfile2 filepath) (let ((port (open-input-file filepath))) (let loop ((contents '()) (c (read-char port))) (if (eof-object? c) (begin (close-input-port port) (list->string contents)) (loop (append contents (cons c '())) (read-char port))))))
これは、読み込んだ文字をいったんリストにして、最後に文字列に変換している。先の例に比べ、文字列の領域を毎回アロケートしなくてよいので効率がよい、かも知れない。
だけどやっぱり気になるのは、Java の例で見てもらうと:
public String readfile2(String filepath) throws Exception { ArrayList<Charactor> contents = new ArrayList<Charactor>(); FileInputStream fis = new FileInputStream(filepath); int b; while ((b = fis.read()) != -1) { contents.add((char)b); } fis.close(); return new String(contents.toArray()); }
わざわざリストに入れて、文字列に直している点。
R5RS を見渡しても、ファイルからデータを読み込む手続きは read-char か peek-char しかなくて、以下のようにガバッと全部読み込むことができない。
public String readfile3(String filepath) throws Exception { FileInputStream fis = new FileInputStream(filepath); byte[] b = new byte[fis.available()]; // ガバッと確保 fis.read(b); // ガバッと読み込み fis.close(); return new String(b); }
ということで、任意のファイルからデータを読み出す方法はわかったけど、わからないのは以下の点。
- onen-input-file でファイルが開けなかった場合、どうやってエラーをハンドルすればよいのか?
- 読めなかった場合は処理がとまってしまう
- ファイルが存在するかどうかを事前に知る方法はないものか?
- 開いてみてエラーが出たらわかる
- ファイルのサイズを事前に知る方法はないのか?
- 全部読み込んだらサイズがわかる
- R5RS では read-char は一文字と謳っているが、これは 1 バイトのことなのか?
- readfile と readfile2 は、どちらもそれほど変わらないのか?
- ファイルがメガオーダーの場合、readfile はメガオーダーのメモリを 1 文字読み込むたびに確保するので、悪い方法のように思える