Lesson 5: ファイル操作

今回は、プログラミングにおいて忘れてはいけないファイルの操作について説明します。
そうね、ファイルの読み書きやログファイルの操作って必要よね。(いつも通りこの色は美樹ちゃん)
それから、今まで省略してきた例外処理についても少し触れてみます。

Section 1: アプリケーションからファイルをアクセスする

ファイルの入出力などを取り扱うには、java.io パッケージを利用します。これらのパッケージは、外部ファイルや記憶装置などに対してデータの入出力が行えます。

Lession 4 で用いたプログラムを、ボタンを押したら入力したメッセージをファイルに書き込み、その後すぐに同じファイルから内容を出力するプログラムに改変します。

// Import Statements
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
 
class FileIO extends JFrame implements ActionListener {
    // Instance Variables (Field)
    JLabel text;
    JButton button;
    JPanel panel;
    JTextField textField;
    private boolean clickFlag = true;
 
    // Constructor
    FileIO() {
        text = new JLabel("Please type a message:");
        button = new JButton("Save Messages: text.txt");
        button.addActionListener(this);
        textField = new JTextField(25);
 
        panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.setBackground(Color.white);
        getContentPane().add(panel);
        panel.add(BorderLayout.NORTH, text);
        panel.add(BorderLayout.CENTER, textField);
        panel.add(BorderLayout.SOUTH, button);
    }
 
    // Event Handling
    public void actionPerformed(ActionEvent event){
        Object source = event.getSource();
        if (source.equals(button)) {
            String messages = null;
            if (clickFlag){
                // Write to file
                try {
                    String text = textField.getText();
                    String oFileName = "text.txt";
                    FileOutputStream out = new FileOutputStream(oFileName);
                    PrintWriter outpw = new PrintWriter(new OutputStreamWriter(out, "Shift_JIS"));
                    outpw.println(text);
                    outpw.close();
                    out.close();
                } catch(java.io.IOException e) {
                    System.out.println("Cannot write to text.txt");
                }
                // Read from file
                try {
                    String iFileName = "text.txt";
                    FileInputStream in = new FileInputStream(iFileName);
                    BufferedReader inbr = new BufferedReader(new InputStreamReader(in, "JISAutoDetect"));
                    messages = inbr.readLine();
                    inbr.close();
                    in.close();
                } catch(java.io.IOException e) {
                    System.out.println("Cannot read from text.txt");
                }
                // Clear text field
                textField.setText("");
                // Display text read from file
                text.setText("Retrieved from file: text.txt");
                textField.setText(messages);
                button.setText("Reset");
                clickFlag = false;
            } else {
                // Save text to file
                text.setText("Please type a message:");
                textField.setText("");
                button.setText("Save Messages: text.txt");
                clickFlag = true;
            }
        }
    }
 
    // Main Method
    public static void main(String[] args) {
        FileIO frame = new FileIO();
        frame.setTitle("File IO Sample");
        WindowListener winListener = new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        };
 
        frame.addWindowListener(winListener);
        frame.pack();
        frame.setVisible(true);
   }
}

ソースをコンパイルして実行すると、以下の画面が表示されます。

Prog1 起動時の画面です。
Prog2 真ん中のフィールドにメッセージを入力し、"Save Messages"ボタンをクリックします。text.txt ファイルに、入力したメッセージが書き込まれます。
Prog3 text.txt ファイルに書き込まれたメッセージを読み込み表示しています。

Section 2: ファイルの入出力

フィールドやコンストラクタは、前回にくらべテキストフィールドに関するコンポーネントが増えています。

textField = new JTextField(25);

大きな変更はファイルの操作に関する部分である actionPerformed で、それ以外の部分は特に難しい変更はしていないので説明を省略します。

// Event Handling
public void actionPerformed(ActionEvent event){
    Object source = event.getSource();
    if (source == button) {
        String messages = null;
        if (clickFlag){
            // Write to file
            try {
                String text = textField.getText();
                String oFileName = "text.txt";
                FileOutputStream out = new FileOutputStream(oFileName);
                PrintWriter outpw = new PrintWriter(new OutputStreamWriter(out, "Shift_JIS"));
                outpw.println(text);
                outpw.close();
                out.close();
            } catch(java.io.IOException e) {
                System.out.println("Cannot write to text.txt");
            }
            // Read from file
            try {
                String iFileName = "text.txt";
                FileInputStream in = new FileInputStream(iFileName);
                BufferedReader inbr = new BufferedReader(new InputStreamReader(in, "JISAutoDetect"));
                messages = inbr.readLine();
                inbr.close();
                in.close();
            } catch(java.io.IOException e) {
                System.out.println("Cannot read from text.txt");
            }
            // Clear text field
            textField.setText("");
            // Display text read from file
            text.setText("Retrieved from file: text.txt");
            textField.setText(messages);
            button.setText("Reset");
            clickFlag = false;
        } else {
            // Save text to file
            text.setText("Please type a message:");
            textField.setText("");
            button.setText("Save Messages: text.txt");
            clickFlag = true;
        }
    }
}

textField に入力された文字列を取り出します。

String text = textField.getText();

書き込み用にファイルを用意しますが、サンプルプログラムのようなファイルの指定ではプログラムを実行したディレクトリーにファイルが作成されます。

String outputFileName = "text.txt";

ファイルの場所を指定する場合、System.getProperty メソッドや File.separatorChar 利用してシステムに依存しないディレクトリーを指定できます。

String outputFileName = System.getProperty("user.home") + File.separatorChar + "text.txt";

上記のような指定のとき、ユーザーアカウントが shin なら以下のように解釈されます(例)。

UNIX /home/shin/text.txt
Windows xp C:\Documents and Settings\shin\text.txt

通常、ファイルの入出力を行うには、FileInputStream/FileOutputStream などといったクラスを利用してファイルを開き、メッセージを read/write メソッドなどを使用して読み書きをし、close メソッドを使用してファイルを閉じます。しかし、テキストファイルを扱う場合、Javaの内部で扱っているUNICODEから JIS, Shift_JIS, EUC-JPなど必要に応じて変換しなくてはならないときがあります。その場合、InputStreamReader/OutputStreamWriter クラスを利用します。このサンプルでは、変換を行うことがあることを考慮してInputStreamReader/OutputStreamWriter クラスを利用しています。システムデフォルトのコードを利用するので変換の必要が無というなら、InputStreamReader/OutputStreamWriter クラスを使う必要はありません。

ファイルにメッセージを書き込むには、FileOutputStream クラスを利用して書き込み用のファイルを指定し、OutputStreamWriter クラスを使用して文字コードを変換します。その後、write メソッドを使って文字をファイルに書き込んでも良いのですが、パフォーマンスなどを考慮してBufferedWriterクラスや PrintWriterクラスを用います。テキストファイルの出力は、このパターンを覚えておくと良いかもしれません。

FileOutputStream   out   = new FileOutputStream(oFileName);
OutputStreamWriter outw  = new OutputStreamWriter(out, "Shift_JIS");
PrintWriter        outpw = new java.io.PrintWriter(outw);

これは、実際には効率を良くするためラッピングを利用して以下のように記述します。

PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(oFileName), "Shift_JIS"));

同じように、ファイルからメッセージを取り出すには、FileInputStream クラスを利用して読み込み用のファイルを指定し、InputStreamReader クラスを使用して文字コードを変換します。その後、read メソッドを使って文字をファイルに書き込んでも良いのですが、パフォーマンスなどを考慮して BufferedReaderクラスを用います。テキストファイルの入力は、このパターンを覚えておくと良いかもしれません。

FileInputStream   in   = new FileInputStream(itFileName);
InputStreamReader inr  = new InputStreamReader(in, "JISAutoDetect");
BufferedReader    inbr = new BufferedReader(inr);

これは、実際には効率を良くするためラッピングを利用して以下のように記述します。

BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(iFileName), "JISAutoDetect"));

サンプルでは、見やすくするためにラッピングを数回に分けて記述しています。

日本語の文字コード(charset) についてリストを作っておきます。詳細は Java のサイトにあります。

ISO-2022-JP
ISO2022JP
JISコード(ISO-2022-JP)
Shift_JIS
SJIS
シフトJISコード
EUC-JP
EUC_JP
日本語EUCコード
UTF-8 Unicode(UTF-8
UTF-16 Unicode(UTF-16)
JISAutoDetect 文字コードの自動判定(Readerクラスのみ利用可)

その後は、作成したファイルのオブジェクトに対してreadLine メソッドやprintlnメソッドによりメッセージの読み書きを行っています。最後に close メソッドでファイルをクローズします。

Section 3: 例外処理

例外処理とは、プログラムを安全に実行させていくために、遭遇するかもしれないエラーを処理することです。try - catch というブロックを利用することで例外処理を記述することが出来ます。try ブロックで記述された処理でエラーが発生した場合、それに応じた例外が発生します。その例外を catch ブロックで処理します。発生した例外に対する catch ブロックが存在しない場合、例外を処理することが出来ません。

サンプルでは、java.io.IOException という入出力に関する例外すべてを拾っています。

Section 4: アプレットでファイルをアクセスする

アプレットでは、通常クライアントのファイルをアクセスすることが出来ません。クライアントのファイルをアクセス可能にするには、ポリシーを設定する必要があります。セキュリティー上、クライアントのファイルをアクセスさせるアプレットの利用には注意が必要です。

// Import Statements
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import java.applet.Applet;
import java.io.*;

public class FileIOApplet extends JApplet implements ActionListener {
    JLabel text;
    JButton button;
    JPanel panel;
    JTextField textField;
    private boolean clickFlag = true;
 
    public void init() {
        getContentPane().setLayout(new BorderLayout(1, 2));
        getContentPane().setBackground(Color.white);
        text = new JLabel("Please type a message:");
        button = new JButton("Save Messages: text.txt");
        button.addActionListener(this);
        textField = new JTextField(25);
        getContentPane().add(BorderLayout.NORTH, text);
        getContentPane().add(BorderLayout.CENTER, textField);
        getContentPane().add(BorderLayout.SOUTH, button);
    }
    public void start() {
        System.out.println("starting...");
    }
    public void stop() {
        System.out.println("stopping...");
    }
    public void destroy() {
        System.out.println("destroying...");
    }

    public void actionPerformed(ActionEvent event){
        Object source = event.getSource();
        if (source.equals(button)) {
            String messages = null;
            if (clickFlag) {
                try {
                    // write to file
                    String text = textField.getText();
                    String outputFileName = "text.txt";
                    FileOutputStream out = new FileOutputStream(outputFileName);
                    PrintWriter outpw = new PrintWriter(new OutputStreamWriter(out, "Shift_JIS"));
                    outpw.println(text);
                    outpw.close();
                    out.close();
  
                    // read from file
                    String inputFileName = "text.txt";
                    FileInputStream in = new FileInputStream(inputFileName);
                    BufferedReader inbr = new BufferedReader(new InputStreamReader(in, "JISAutoDetect"));
                    messages = inbr.readLine();
                    inbr.close();
                    in.close();
                } catch(java.io.IOException e) {
                    System.out.println("Cannot access text.txt");
                }
                // Clear text field
                textField.setText(""); 
                // Display text read from file
                text.setText("Retrieved from file: text.txt");
                textField.setText(messages);
                button.setText("Reset");
                clickFlag = false;
            } else {
                // Save text to file
                text.setText("Please type a message:");
                button.setText("Save Messages: text.txt");
                textField.setText("");
                clickFlag = true;
            }
        }
    }
}

アプレットの実行には、実行するクラスファイルの指定やパラメータ等を定義した HTMLファイル(fileio.html)が必要になります。

<HTML>
<BODY>
<APPLET CODE="FileIOApplet.class" WIDTH="200" HEIGHT="100">
</APPLET>
</BODY>
</HTML>

さらに、クライアント側のファイルをアクセスするためポリシーの設定(policy.txt)が必要になります。

grant {
     permission java.io.FilePermission "text.txt", "read, write";
};

Java対応のブラウザ(Javaの実行が可能な状態になっている)にそのまま読み込ませるか、appletviewer を使って実行します。

D:\tmp> appletviewer -J-Djava.security.policy=policy.txt fileio.html
starting...
Prog4
Prog5
Prog6
起動時の画面です。 真ん中のフィールドにメッセージを入力し、"Save Messages"ボタンをクリックします。text.txt ファイルに、入力したメッセージが書き込まれます。 text.txt ファイルに書き込まれたメッセージを読み込み表示しています。

ウィンドウを閉じてアプレットを終了する。

stopping...
destroying...
D:\tmp> 

ここでは、すべて "D:\tmp"ディレクトリーで作業しているのでポリシーの設定をカレントファイルに対して行っていますが、実際には policytool コマンドを使用してポリシーの設定を行ってください。

Policy Tool

[Prev] [Next]