ファイル読み込み

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 文字読み込むたびに確保するので、悪い方法のように思える