これまで,FileReaderでファイルを読んできました。このクラスはファイルから文字単位に読んでいくためのクラスです。ところがこれでWindowsで作られたテキストファイルを読むとうまく読むことができません。
これはLinuxとWindowsではテキストファイルの日本語の文字の保存方法に違いがあるからです。
次のようなファイルをつくり、xcode.txtという名前で保存します。
ABC。あいう
これを以前作ったDump.javaで内容を見てみます.
$ java Dump xcode.txt 41 42 43 e3 80 82 e3 81 82 e3 81 84 e3 81 86 a
Windowsで同じ文字を保存したファイルをDump.javaで調べてみます。
$ java Dump xcode.sjis 41 42 43 81 42 82 a0 82 a2 82 a4 a
Shift_JISのファイルはgeditでも作れますが、xcode.sjis という名前で用意しました。Shift_JISのxcode.sjisを右クリックから一旦保存して使ってください。普通のクリックをしてしまうと環境によってはブラウザで開いてしまうかも知れません。
LINUXで作ったファイルはUTF-8という形式、WindowsではShift_JISという形式になっています。文字コードは次のようになっています。
A | B | C | 。 | あ | い | う | LF | |
UTF-8 | 41 | 42 | 43 | e3 80 82 | e3 81 82 | e3 81 84 | e3 81 86 | a |
Shift_JIS | 41 | 42 | 43 | 81 42 | 82 a0 | 82 a2 | 82 a4 | a |
半角文字部分は共通。日本語の文字部分が異なっています。
区 点 JIS SJIS EUC UTF-8 UTF-16 字 04 01 2421 829F A4A1 E38181 3041 ぁ 04 02 2422 82A0 A4A2 E38182 3042 あ 04 03 2423 82A1 A4A3 E38183 3043 ぃ 04 04 2424 82A2 A4A4 E38184 3044 い 04 05 2425 82A3 A4A5 E38185 3045 ぅ 04 06 2426 82A4 A4A6 E38186 3046 う 04 07 2427 82A5 A4A7 E38187 3047 ぇ 04 08 2428 82A6 A4A8 E38188 3048 え 04 09 2429 82A7 A4A9 E38189 3049 ぉ 04 10 242A 82A8 A4AA E3818A 304A お 04 11 242B 82A9 A4AB E3818B 304B か 04 12 242C 82AA A4AC E3818C 304C が 04 13 242D 82AB A4AD E3818D 304D き 04 14 242E 82AC A4AE E3818E 304E ぎ 04 15 242F 82AD A4AF E3818F 304F く 04 16 2430 82AE A4B0 E38190 3050 ぐ 04 17 2431 82AF A4B1 E38191 3051 け 04 18 2432 82B0 A4B2 E38192 3052 げ 04 19 2433 82B1 A4B3 E38193 3053 こ 04 20 2434 82B2 A4B4 E38194 3054 ご 04 21 2435 82B3 A4B5 E38195 3055 さ
これをFileReaderで読んでみます。FileReaderで読み、コードポイントを表示するプログラムを作ります。各種文字コードは16進数(0,1,2,...9,a,b,c,d,e,fを使った数の表記)を使うのが一般的なので、Dumpと同じく16進数も表示するようにしています。Integer.toHexString(c)がそれです。その後10進数も書いています。
import java.io.*; public class ShowCodePoint { public static void main( String[] args ) { String fname =args[0]; try { FileReader in = new FileReader(fname); BufferedReader inb = new BufferedReader(in); int c; while ((c = inb.read()) != -1) { System.out.print( Integer.toHexString(c) ); //16進で表示 System.out.print( " " ); System.out.println( c ); //10進でも表示 } inb.close(); in.close(); } catch (IOException e) { System.err.println( "ファイルがないのでは?" ); } } }
実行結果です
$ java ShowCodePoint xcode.txt 41 65 42 66 43 67 3002 12290 3042 12354 3044 12356 3046 12358 a 10
12290は、"。"を表すコードでした。javaのコードポイントはUTF-8でもShift_JISでもありません。UTF-16というものです。
Shift_JISのファイルを読み込むと次のようになります。
$ java ShowCodePoint xcode.sjis 41 65 42 66 43 67 fffd 65533 42 66 fffd 65533 fffd 65533 fffd 65533 fffd 65533 fffd 65533 fffd 65533 a 10
これは正しく読めていません。
ではFileReaderはUTF-8で書かれたファイルを読むように作られているのでしょうか。いいえ違います。Windows環境ではShift_JISのファイルが正常に読め、UTF-8のファイルは正常に読めなくなります。
つまり、FileReaderはOSの標準の文字コードで書かれているファイルを読むように設計されているのです。
javaはコンパイルされたプログラム(classファイル)はLinux環境でもWindows環境でもコンパイルし直さなくても実行できます。ですからそれぞれの環境で使っている文字コードをそのまま使うように設計されていた方が便利だからです。
FileReaderはOSの標準(デフォルト)の文字コードで読むクラスです。他の文字コードで読むにはまず、FileInputStream でバイト毎に読んで、InputStreamReader で文字コードを指定して文字に組み立てていきます。
import java.io.*; public class ShowCodePointS { public static void main( String[] args ) { String fname =args[0]; try { FileInputStream is = new FileInputStream(fname); InputStreamReader in = new InputStreamReader(is,"Shift_JIS"); //FileReader in = new FileReader(fname); BufferedReader inb = new BufferedReader(in); int c; while ((c = inb.read()) != -1) { System.out.print( Integer.toHexString(c) ); System.out.print( " " ); System.out.println( c ); } inb.close(); in.close(); } catch (IOException e) { System.err.println( "ファイルがないのでは?" ); } } }
これでShift_JISのファイルが読めます。
$ java ShowCodePointS xcode.sjis 41 65 42 66 43 67 3002 12290 3042 12354 3044 12356 3046 12358 a 10
その代わりUTF-8が読めなくなりました。
$ java ShowCodePointS xcode.txt 41 65 42 66 43 67 7e32 32306 3085 12421 2260 8800 7e3a 32314 fffd 65533 2267 8807 a 10
(この状況はWindows上でFileResderで読む時の状態と全く同じです)
InputStreamReader は、バイトデータの並びを読み込んで、指定された文字セットを使用して文字に変換します。文字セットの指定が省略されると、javaのデフォルトの文字セットに変換します。
つまり、文字セットの指定が省略されるとFileReaderと同じ機能になります。
OutputStreamWriter は指定された文字セットに変換し、バイトデータの並びとして書き出します。
次のプログラムはUTF-8でかかれたxcode00.txtというファイルから読み込み、EUC-JPに変換してxcode01.txtに書きます。
import java.io.*; public class Xcode { public Xcode(String ifname, String ofname){ try { FileInputStream in = new FileInputStream(ifname); InputStreamReader inx = new InputStreamReader(in,"UTF-8"); FileOutputStream out = new FileOutputStream(ofname); OutputStreamWriter outx = new OutputStreamWriter(out,"EUC-JP"); //読み込みと書き込み int c; int ct = 0; while ((c = inx.read()) != -1) { outx.write(c); ct++; } System.out.println("以上" + ct + "文字"); inx.close(); in.close(); outx.close(); out.close(); } catch (IOException e) { System.out.println( ifname + " がないのでは?" ); } } public static void main(String[] args) { Xcode x = new Xcode("xcode00.txt","xcode01.txt"); } }
他の文字コードを使いたい時は下の表を参考にしてください。ただし、最初の2つISO-8859-1とUS-ASCIIは日本語に対応しません。
java.nioでの名前 | java.ioでの名前 これを使用 |
説明 |
ISO-8859-1 | ISO8859_1 | ISO 8859-1, Latin Alphabet No. 1 |
US-ASCII | ASCII | American Standard Code for Information Interchange |
UTF-8 | UTF8 | Eight-bit UCS Transformation Format |
UTF-16 | UTF-16 | Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark |
EUC-JP | EUC_JP | JISX 0201, 0208 and 0212, EUC encoding Japanese |
ISO-2022-JP | ISO2022JP | JIS X 0201, 0208, in ISO 2022 form, Japanese |
Shift_JIS | SJIS | Shift-JIS, Japanese |
windows-31j | MS932 | Windows Japanese |
上記の Xcode.java をつくり実行してみなさい
適当なテキストファイル xcode00.txt を作って、実行し、xcode01.txt ができていることを確認しなさい。
xcode01.txtをエディタで開いて内容を確認しなさい。
次の二つを実行してファイルの内容を比較しなさい。
java Dump xcode00.txt java Dump xcode01.txt
$ javac Xcode.java $ java Xcode 以上8文字 $ java Dump xcode00.txt 41 42 43 e3 80 82 e3 81 82 e3 81 84 e3 81 86 a 以上16バイト $ java Dump xcode01.txt 41 42 43 a1 a3 a4 a2 a4 a4 a4 a6 a 以上12バイト $
まず、xcode00.txt に xcode.txt とおなじ、「ABC。あいう」と入れ、出力する文字コードを替えて、Dumpし、下の空欄をうめなさい。ISO-2022-JPは素直にいかないかも知れません。
A | B | C | 。 | あ | い | う | LF | |
UTF-8 | 41 | 42 | 43 | e3 80 82 | e3 81 82 | e3 81 84 | e3 81 86 | a |
EUC-JP | ||||||||
UTF-16 | ||||||||
ISO-2022-JP |
ABC。あいう 亜唖娃阿123 dEはひxY