ファイルの入出力


良いプログラムの条件としては,プログラムが読みやすい(理解しやすい), 保守しやすい,移植性が高い,メモリ等のリソースの消費が少ないなど,さま ざまな条件があるが,実行速度が速いというのも重要な条件の一つである.

また,実行速度はプログラマの質による差異が.速いプログラムと遅いプロ グラムでは、100倍、1000倍速度が違うことは珍しくない。このような速度の 差は,使用するプログラミング言語の違いにより生じることもあるが,あるこ とをやる際のアルゴリズム(計算法)による違いによる場合が多い.

ここでは,全順序関係の成り立つ要素を順序関係に従って整列しなおす(これ をソートと呼ぶ)プログラムを例に,アルゴリズムと実行速度の関係について, 学ぶことにする.


ファイルに対する入出力

本題とは関係ないが,これまでやり残していたファイル入出力の方法を説明 する.なお,ファイル入出力を,Applet中でやることは制限されている.この 制限は 1. Netscape上で http:/ でアクセスする場合 2. Netscape上で file:/ でアクセスする場合 3. appletviewerで file:/ でアクセスする場合 の順で制限が緩くなっている.1で自由にファイルの読み書きを許すと, NetscapeでJavaアプレットのあるページを見ただけで,ファイルを盗み見され たり,破壊されてしまう心配があるので,制限をするのは必然である.

リダイレクトによるファイルに対する入出力

Unix システムの特徴として,普通にキーボードから出力して画面上に表示す ることを想定して作られた CUI(Character-base User Interface) プログラム も,'< filename'とやってファイルから読み込ませたり,'> filename'として, ファイルに出力したり '| command' として別のコマンドの入力にしたりする ことができるということがある.これを shellのリダイレクト機能という. Javaプログラムも例外ではない.次のプログラムは,単純に標準入力から入っ た行を標準出力に出力するプログラムであり,普通に
% java Cat
と実行すると,キーボードから入力して画面上に出力するプログラムになるが (終了はCtrl-dでおこなう),
% java Cat < Cp.java > tmp
と実行するとCat.javaから読み込んだファイルを tmpに
 // BufferedReaderなどを使うので,java.io.*を import する.
import java.io.*;
class Cat {
   // 入出力の途中で,IOExceptionの例外が起きる可能性があるので,
   // throws IOException をつける.
  public static void main(String [] args) throws IOException{
    BufferedReader is=new BufferedReader(new InputStreamReader(System.in));
    String s;
    while((s=is.readLine())!=null){
      System.out.println(s);
    }
  }
}

ファイルストリームの作成

小さなツールを作る分には,shellのリダイレクト機能だけで間に合ってしま うが,以下のようなプログラムを作るためにはファイル操作に関する API(APplication Interface)を呼ぶ必要がある. そこで,Javaのファイル操作に関する API にすこし触れてみる.Javaでは, ファイル名からストリームを作成して,そこに対して入出力をおこなう.ファ イルに対する標準的な入力用のストリームのクラスは FileReader 標準的な出力用のストリームのクラスは, FileWriter のクラスであり,それぞれファイル名の文字列からス トリームを作る.

入力用のストリームは,FileReaderから BufferedReaderを作ることにより,行単位の入出力をおこなうことができ る.出力用のストリームはFileWriterから PrintWriterを作ることにより,println等が実行できる.

これを使った Cpプログラムを見てみる.

 // BufferedReaderなどを使うので,java.io.*を import する.
import java.io.*;
class Cp {
   // 入出力の途中で,IOExceptionの例外が起きる可能性があるので,
   // throws IOException をつける.
  public static void main(String [] args) throws IOException{
    if(args.length!=2) usage();
    BufferedReader is=new BufferedReader(new FileReader(args[0]));
    PrintWriter ps=new PrintWriter(new FileWriter(args[1]));
    String s;
    while((s=is.readLine())!=null){
      ps.println(s);
    }
    is.close();
    ps.close();
  }
  static void usage(){
    System.err.println("使い方: java Cp コピー元のファイル コピー先のファイル");
    System.exit(1);
  }
}
不要になったストリームを close するのを忘れないように.
java Cp Cp.java soko
を実行すると Cp.javaの内容が sokoというファイルにコピーされる.

ソートのための準備

Unixプログラムには,sortというコマンドがある.中身は
% man sort
で確認できると思う.ここでは,その最も単純な行の先頭からの辞書順序での ソートをおこなうプログラムを扱ってみる.

まずは準備として,ファイルから読み込んで1行毎の文字列の配列を作成する という部分を見てみる.

 // BufferedReaderなどを使うので,java.io.*を import する.
import java.io.*;
 // Vectorを使うので java.util.* を importする.
import java.util.*;

class SortTest {
    // このメソッドの中で linesの並び替えをおこなう.
  static void sort(String[] lines){
  }
  //  IOExceptionを catchしていないので,throws IOExceptionをつける.
  public static void main(String args[]) throws IOException{
    // ファイル名と思って BufferedReader
    BufferedReader is=new BufferedReader(new FileReader(args[0]));
    // 何行になるかわからないので,Vectorで記憶しておく
    Vector v=new Vector();
    String s;
    // 1行読み込んでファイルの終端に到達しない間
    while((s=is.readLine())!=null){
      // Vectorの最後に要素を加える
      v.addElement(s);
    }
    // 入力ストリームを閉じる
    is.close();
    // Vectorから配列に変換するために,領域を確保する
    String[] lines=new String[v.size()];
    // Vectorから配列に変換する.
    v.copyInto(lines);
    // ソートする
    sort(lines);
    // ソートした結果を出力する.
    for(int i=0;i< lines.length;i++)
      System.out.println(lines[i]);
  }
}

次に進む