文字ストリームと文字符号化
java.ioパッケージのReaderクラス、FileReaderクラス、Writerクラス、FileWriterクラスといった文字ストリームを使ってファイルの読み書きをするプログラムはすでに説明しました。それらは文字を扱っていましたが、文字符号化については特に指定をしませんでした。指定しない場合はデフォルトの文字符号化が適用される点は意識しておきましょう。環境にもよりますが、Windowsを使っている場合はMS932、Fedora Core5 Linuxを使っている場合はUTF-8、Debian GNU/Linuxを使っている場合はEUC-JPとなるはずです。現在使用されているデフォルトの文字符号化の値を知るためには、次のようなプログラムを使って確認することができます。java.nio.charset.Charsetクラスを使う方法と、java.io.InputStreamReaderやjava.io.OutputStreamWriterのgetEncodingメソッドを使う方法があります。ただし、文字符号化名には別名があるので注意しましょう。例では別名の一覧も表示しています。完全なプログラムはSample750.javaになります。
String defaultEncoding = java.nio.charset.Charset.defaultCharset().name(); System.out.println(defaultEncoding); System.out.println(""); OutputStreamWriter writer = new OutputStreamWriter(System.out); defaultEncoding = writer.getEncoding(); System.out.println(defaultEncoding); System.out.println("");< for (String de: Charset.defaultCharset().aliases()) { System.out.println(de); }
実行結果を見て分かるように、Charsetでは「windows-31j」ですが、OutputStreamWriterでは「MS932」となっています。「MS932」は「windows-31j」の別名に含まれていますから、一致していることが分かります。
コラム
例ではJava2 SE 5.0で新しく導入された拡張for文を使っています。これはjava.util.Iteratorを使ったfor文と簡単に対応できますから、すぐに慣れて使えるようになります。
for (String alias: Charset.defaultCharset().aliases()) { System.out.println(alias);
これは、下記のjava.util.Iteratorを使ったfor文と同じ処理になります。
for (Iterator it = Charset.defaultCharset().aliases().iterator(); it.hasNext(); ) { String alias = (String)it.next(); System.out.println(alias); }
ちなみに、Java2 SE 5.0で新しく導入されたGenericsを使ったIteratorでは要素の型を指定できますから、それを使って書き直すと下記になります。
for (Iterator<String> it = Charset.defaultCharset().aliases().iterator(); it.hasNext(); ) { String alias = it.next(); System.out.println(alias); }
デフォルトの文字符号化が使用されて動作するのは同じプラットフォームでプログラムを動作させる場合には便利なのですが、独自の文字符号化を決めたいときもあります。マルチプラットフォーム対応の独自アプリケーションを作成する場合には対象とするファイルについて入出力における文字符号化を固定してプラットフォームに依存しないようにさせたいはずです。そんなときのために、バイトストリームと文字ストリームの間をつなぐクラスとして、java.ioパッケージにはInputStreamReaderクラスとOutputStreamWriterクラスが用意されています。これらのコンストラクタでは使用するバイトストリームと適用する文字符号化を指定することができます。完全なプログラムはSample760.javaになります。
OutputStream os = new FileOutputStream(new File("sample2.txt")); Writer writer = new OutputStreamWriter(os, "UTF-8"); writer.write("\u3042\u3044\u3046\u0041\u0042\u0043"); InputStream is = new FileInputStream(new File("sample2.txt")); Reader reader = new InputStreamReader(is, "UTF-8");
適用する文字符号化がOutputStreamWriterクラスとInputStreamReaderクラスで一致していないと文字化けが発生します。例えば、InputStreamReaderクラスのコンストラクタの方だけUTF-8ではなくWindows-31jを指定すると次のようになります。完全なプログラムはSample761.javaになります。
Reader reader = new InputStreamReader(is, "Windows-31J");
InputStreamReaderクラスとOutputStreamWriterクラスは、既存のクラスがバイトストリームを提供している場合に、そのストリーム内を流れるデータを文字データとして扱うように自作プログラムで利用したいといった要求にも対応できます。例えば、java.lang.Systemのoutフィールドはjava.io.PrintStreamというちょっと特殊なバイトストリームです。これを使用するときに、文字符号化を考慮した文字データ出力をしたいといった場合に便利です。
コラム
対象とするファイルがどのような文字符号化を使っているのかあらかじめ分かっている場合には、文字ストリームオブジェクトのコンストラクタで指定をすることができますが、例えばXHTMLファイルのようにファイル自体にそれが記述されている場合には注意が必要になります。この場合は、XMLの仕様に従って指定された文字符号化を読み取り、かつ、HTMLのメタタグで指定された文字符号化と一致しているかを確認する必要があるため、単純に確定することができません。
改行コードについても注意が必要です。歴史的経緯により、改行コードはOSによって異なります。例えばWindowsでは「\r+\n」ですが、Linuxでは「\n」です。ここで、「\r」とは復帰コードCR(Carriage Return)のことで、「\n」は改行コードLF(Line Feed)のことです。プラットフォーム依存の改行コードを出力したい場合には、java.lang.SystemクラスのgetPropertyメソッドを使って「line.separator」のシステムプロパティ値を取得して、それを使用します。java.io.BufferedWriterクラスのオブジェクトを使っている場合には、newLineメソッドを使うという手もあります。次のプログラムをWindowsで実行すると「13 10」と出力しますが、Linuxで実行すると「10 」と出力します。完全なプログラムはSample770.javaになります。
String newLine = System.getProperty("line.separator"); byte[] nl = newLine.getBytes(); for (int i=0 ; i<nl.length ; i++) { System.out.print(nl[i]+" "); }
さて、「EclipseでJavaに強くなる」ということで、コンピュータによる計算方法の基礎や、プログラミングへの理解がさらに深まるようなテーマを取り上げて解説をしました。プログラミングの基礎への理解が深まることで、Javaに強くなってもらえたはずです。入出力、例外、型、文字符号化、ファイル操作といったテーマを通じて、1ステップ上の文法事項、基本事項について解説してきました。本連載の内容を理解することにより、プログラムによって計算された結果をメモリ上からディスクへ保存し、それを利用することができるようになれたはずです。計算の途中でデータの値がおかしくなったり、文字化けが発生したり、ファイルへ保存した値がおかしくなったりしても、学んだことを思い出して1つ1つデータをチェックしていけば、問題の原因を追究することができるはずです。Javaもバージョンアップによってパワーアップしていますが、基本は変わりません。これからも、オブジェクト指向プログラミングを実現する土台ともいえる基本をしっかり身に付けて、真のプログラミング力向上を目指してください。最後に、連載を楽しみにして、ここまで読んでいただいた読者の皆さんに感謝いたします。
筆者紹介
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。Ja-Jakartaプロジェクト(http://www.jajakarta.org/)へ参加し、コミッタの一員として活動を支えている。また、長野県の地域コミュニティである、SSS(G)(http://www.sssg.org/)やbugs(J)(http://www.bugs.jp/)の活動へも参加している。
Copyright © ITmedia, Inc. All Rights Reserved.