これまで既にJDK1.0でアプリケーションを開発していて、新しくJDK1.1で日本語対応 をしたいというケースがあると思います。一概には言えないのですが、 自分がこれまで何をしていて、これから何をしたいのかが明確になっていれば、 驚くほど簡単に日本語対応が可能となることがあります。 ちょっと手をいれるだけで、魔法のように、プログラム全体の機能が 変わってしまうというのは、object指向言語の醍醐味ともいうべきものです。 ポイントは、これまで何回か述べて来たように、入出力でbyte streamとchar streamを 区別して、後者に移行するということです。
実は、日本語を使っていたとしても一切変更の必要のない例もあるのです。 従来のJDK1.0でのJavaで書かれた copyプログラムを、JDK1.1にバージョンアップ することを考えてみましょう。次のような、byte列をストリームから読み取り、 byte列のまま書き出すようなプログラムは、コンバータの世話には なっていませんので、変更の必要はありません。
============================================================================ /* jdk1.0 でも jdk1.1でも変更の必要の無い例 */ class ReadWriteRawByte { public static void main (String argv[]){ try { FileInputStream from = new FileInputStream(argv[0]); FileOutputStream to = new FileOutputStream(argv[1]); int buflen = 80 ; byte buff[] = new byte[buflen] ; int len ; while( (len = from.read(buff,0,buflen)) != -1 ){ to.write(buff,0,len); } } catch (Exception e ){ System.err.println("Exception :" + e); } } } ============================================================================
ただ、次のようなDataInputStreamクラスとPrintStreamクラスを使った プログラムは、多少の変更が必要です。この copyプログラムは、JDK1.0では 正しく動きますが、JDK1.1でコンパイルしなおすと、日本語を正しく表示できません。
============================================================================ /* DataInputStream, PrintStream は、変更が必要です */ class Obsolete { public static void main (String argv[]){ try { FileInputStream fin = new FileInputStream(argv[0]); DataInputStream from = new DataInputStream(fin); FileOutputStream fout = new FileOutputStream(argv[1]); PrintStream to = new PrintStream(new BufferedOutputStream(fout)); String line ; while( (line = from.readLine()) != null ){ to.println(line); } to.flush(); } catch (Exception e ){ System.err.println("Exception :" + e); } } } ============================================================================
変更するのは簡単です。DataInputStreamをBufferdReaderに、PrintStreamを PrintWriterに、それぞれクラスを変更すれば良いのです。これで、自動的に コンバータが組み込まれたストリームを使う事が出来ます。コードや、コンバータ を意識する必要はありません。
============================================================================ /* DataInputStream を BufferdReader に、PrintStreamをPrintWriterに */ class ReaderAndWriter { public static void main (String argv[]){ try { FileReader fin = new FileReader(argv[0]); BufferedReader from = new BufferedReader(fin); FileWriter fout = new FileWriter(argv[1]); PrintWriter to = new PrintWriter( new BufferedWriter( fout )); String line ; while( (line = from.readLine()) != null ){ to.println(line); } to.flush(); } catch (Exception e ){ System.err.println("Exception :" + e); } } } ============================================================================
java.ioパッケージには、StreamTokenizerというクラスが用意されています。 appletしか作ってない人には、あまり馴染みが無いかも知れませんが、ファイル 上のデータを読み書きするプログラムをJavaで作るには、不可欠の道具です。 Cのscanfのように使えますし、原始的なawkやperlとしても使える、 ストリーム中のデータを、あるかたまりにして切り出すツールです。 JDK1.0でのStreamTokenizerは、日本語への対応が不十分でしたが、今回、 完璧に、日本語への対応を果たしました。
次にあげるのが、JDK1.0でのStreamTokenizerのコンストラクタです。 コンストラクタは、InputStreamを引数にとっています。
======================================================================== /** * Creates a stream tokenizer that parses the specified input * stream. * By default, it recognizes numbers, Strings quoted with * single and double quotes, and all the alphabetics. */ public StreamTokenizer (InputStream I) { .............................. } ========================================================================
次が、新しいJDK1.1での、日本語対応を果たしたStreamTokenizerの コンストラクタです。実は、トリビアルな変更を別にすれば、本質的な 変更と言うべきものはここの部分だけで、日本語対応が完了しています。
======================================================================== /** * Create a tokenizer that parses the character stream that results from * converting the given byte stream into characters according to the * default character encoding. */ public StreamTokenizer(InputStream I) { this(new BufferedReader(new InputStreamReader(I))); } /** * Create a tokenizer that parses the given character stream. */ public StreamTokenizer(Reader I) { .............................. } ========================================================================
三つのことに注目して下さい。
第一。 最初のコンストラクタは、以前のコンストラクタと同じタイプの引数 (InputStream)をとっています。このことで、以前のクラスを利用したプログラムは、 ソースの変更無しでそのまま、JDK1.1のもとでコンパイルし直すことが可能に なります。運が良ければ(というより、自分が何をしているか分かっていれば)、 それだけで、StreamTokenizerクラスを利用したアプリケーションの日本語対応の 仕事は終わりになります。
第二。 最初のコンストラクタは、InputStreamをInputStreamReaderに与えて、 byte streamをcharacter streamに変換しています。ここでは、InputStreamReader の引数が一つしか無いので、defaultのencodingを想定している事が分かります。 あとは、バッファリングをするようにしているだけです。 基本的には、これでStreamTokenizerの日本語対応は終了です。
第三。第二番目のコンストラクタは、Readerクラスを引数にとっています。 これは、「byte streamからchar streamへ」という一般的な指針の当然の 帰結です。もっとも、「日本語対応」と言うことで、InputStreamを引数にとる コンストラクタを、機械的にReaderを引数にとるコンストラクタに置き換えるだけ では、あまり味が無い事が分かりますでしょうか? 二つのコンストラクタが 絶妙です。