日本語化されたStreamTokenizerの簡単なサンプルを紹介しましょう。 このプログラムは、「名前」と「科目」と「点数」からなる、次のような日本語を 含むデータを読み込んで個人毎の平均と科目毎の平均を出力するものです。
入力データ --------------------- 丸山 UNIX概論 67 丸山 Java 90 丸山 NetWork管理 93 McDougal UNIX概論 95 McDougal Java 83 McDougal NetWork管理 85 植田 Java 95 植田 NetWork管理 78 姫宮 UNIX概論 100 姫宮 Java 64 姫宮 NetWork管理 82 藤木 UNIX概論 87 藤木 Java 74 藤木 NetWork管理 95 --------------------- 出力 --------------------- 植田: [Java 95; NetWork管理 78; ] 平均: 86.0 丸山: [UNIX概論 67; Java 90; NetWork管理 93; ] 平均: 83.0 McDougal: [UNIX概論 95; Java 83; NetWork管理 85; ] 平均: 87.0 姫宮: [UNIX概論 100; Java 64; NetWork管理 82; ] 平均: 82.0 藤木: [UNIX概論 87; Java 74; NetWork管理 95; ] 平均: 85.0 --------------------- UNIX概論: [丸山 67; McDougal 95; 姫宮 100; 藤木 87; ] 平均: 87.0 NetWork管理: [丸山 93; McDougal 85; 植田 78; 姫宮 82; 藤木 95; ] 平均: 86.0 Java: [丸山 90; McDougal 83; 植田 95; 姫宮 64; 藤木 74; ] 平均: 81.0 ---------------------
COBOLで解いてもおかしくない問題ですが、ここでのアルゴリズムは、 Hashtable, Vector, EnumerationとJDK1.1で登場した Inner Class を使った、 Javaでしか書けないようなものです。 何度目かの約束違反ですが、残念ながら、プログラムを説明する余裕は 残されていません。興味のある方は、是非、解読して下さい。
import java.io.*; import java.util.*; class test { // Inner Class class PersonalCard{ /* 個人名と点数を書いたカード */ String Name ; int Points; PersonalCard(String s, int n){ Name=s; Points=n; } } // Inner Class class SubjectCard{ /* 科目名と点数を書いたカード */ String Subject; int Points; SubjectCard(String s, int n){ Subject=s; Points=n; } } Hashtable hname = new Hashtable(); /* 個人名毎のテーブル */ Hashtable hsubject = new Hashtable(); /* 科目名毎のテーブル */ String name = null ; String subject = null ; int points = 0 ; test(String filename){ int ret; try { /* Readerをconstructorの引数に。 "SJIS" Converterを指定している */ InputStreamReader in = new InputStreamReader(new FileInputStream(filename), "SJIS"); StreamTokenizer st = new StreamTokenizer(in); while( ( ret=st.nextToken() ) != st.TT_EOF ){ name = st.sval ; /* 名前を読み取る */ ret=st.nextToken(); subject = st.sval ; /* 個人名を読み取る */ ret=st.nextToken(); points = (int)st.nval ; /* 点数を読み取る */ register(name,subject,points); /* データを登録する */ } } catch ( Exception e){ System.out.println(e); } } void register(String name ,String sbj, int p ){ Vector v = null; /* 個人名毎のテーブルから、個人用データのいれものを取り出す */ if ( ( v = (Vector)hname.get(name)) == null ){ v = new Vector(); } /* 個人毎のいれものに、科目名と点数が記入されたカードをいれる */ v.addElement(new SubjectCard(sbj,p)); /* いれものを元に戻す */ hname.put(name, v) ; /* 科目名毎のテーブルから、科目用データのいれものを取り出す */ if ( ( v = (Vector)hsubject.get(sbj)) == null ){ v = new Vector(); } /* 科目毎のいれものに、個人名と点数が記入されたカードをいれる */ v.addElement(new PersonalCard(name,p)); /* いれものを元に戻す */ hsubject.put(sbj, v) ; } void show(){ String key=null; Vector v = null ; System.out.println( "--------------------- " ); for( Enumeration e = hname.keys(); e.hasMoreElements(); ){ key=(String)e.nextElement(); v = (Vector)hname.get(key) ; System.out.print( key + ": ["); showSubject(v); } System.out.println( "--------------------- " ); for( Enumeration e = hsubject.keys(); e.hasMoreElements(); ){ key=(String)e.nextElement(); v = (Vector)hsubject.get(key) ; System.out.print( key + ": ["); showName(v); } System.out.println( "--------------------- " ); } void showSubject(Vector v){ SubjectCard sc; int n = v.size() ; int sum = 0 ; for( int i = 0 ; i < n ; i++){ sc = (SubjectCard)v.elementAt(i); System.out.print(sc.Subject+" "+sc.Points+"; "); sum += sc.Points; } System.out.println("] 平均: "+ ( float )( sum/n )); } void showName(Vector v){ PersonalCard pc; int n = v.size() ; int sum = 0 ; for( int i = 0 ; i < n ; i++){ pc = (PersonalCard)v.elementAt(i); System.out.print(pc.Name+" "+pc.Points+"; "); sum += pc.Points; } System.out.println("] 平均: "+ ( float )( sum/n )); } public static void main(String argv[]){ test t = new test( argv[0] ); t.show(); } }
以前のバージョンでは、getConverter()、ないしは、 getDefault()で、explicitに Converterのインスタンスを獲得して、それを使って コード変換や、Reader/Writerクラスを作っていました。元となる二つのConverter クラスは、java.ioパッケージに属するpublicなクラスで、自分で、新しいコンバータ をプログラムすることも可能でした。
次の例は、僕が作ったものですが、'\uXXXX'
という形式のUnicode表現を、
byte列
として読み込んで、16進のXXXXをJava内部のcharに変換する ByteToCharConverter
クラスを拡大した コンバータと、Java内部のcharコードを、16進数XXXXに変換して、
外部に、'\\ uXXXX'
という形式のbyte列として送り出す CharToByteConverter クラスを
拡大したコンバータです。
ところが、新しいバージョンでは、Converterクラスは、java.ioパッケージから 姿を消してしまいました。もっとも、Converterクラスがなくなったわけでは ないのです。二つのConverterクラスは、sun.ioパッケージに移されただけです。 ただ、Reader/Writerクラスの中で、明示的にConverterクラスを操作することが、 ユーザには出来なくなりました。 Converterを作ることは作れるのですが、それを、入出力のストリームに仕掛ける ことが出来なくなりました。
それを可能にするためには、InputStreamReaderとOutputStreamWriterのprivateな コンストラクタをpublicに変えたクラスを作成すればいいのです。幸い、これらの ソースは提供されていますので、Converterの設定可能な新しいReader/Writerクラスを つくることが出来ます。
package mylib.util; import sun.io.*; public class B2CUni extends ByteToCharConverter { public int convert(byte ab[], int i1, int j1, char ach[], int k1, int i2) throws ConversionBufferFullException { int j2 = k1; int c1, c2, c3; byte buf[] = new byte[4]; for (int k2 = i1; k2 < j1; ) { if (j2 >= i2) throw new ConversionBufferFullException(); c1 = ab[k2]; if ( c1 != '\\ ' ){ ach[j2] = (char)( c1 & 127 ); k2++; j2++; } else { c2=ab[k2+1]; if ( c2 != 'u' ){ ach[j2] = '\\ ' ; ach[j2+1] = (char)c2 ; k2++; k2++; j2++; j2++; } else { System.arraycopy(ab,k2+2,buf,0,4); ach[j2]=(char)( Integer.parseInt( new String(buf), 16) ); k2+=6; j2++; } } } return j2 - k1; } public String getCharacterEncoding() { return "Unicode"; } public int flush(char ach[], int i, int j) { return 0; } public void reset() { } }
package sun.util; import java.io.*; public class C2BUni extends CharToByteConverter { public int convert(char ach[], int i1, int j1, byte ab[], int k1, int i2) throws ConversionBufferFullException { int len; int j2 = k1; String hex = null ; for (int k2 = i1; k2 < j1; ) { if (j2 >= i2) throw new ConversionBufferFullException(); if ( ach[k2] < 127 ){ ab[j2] = (byte)ach[k2] ; k2++; j2++; } else { ab[j2] = (byte)('\\ ') ; ab[j2+1] =(byte)('u'); hex=Integer.toHexString( (int)ach[k2] ); len=hex.length(); ab[j2+2]=(byte)hex.charAt(0); ab[j2+3]=(byte)hex.charAt(1); ab[j2+4]=(byte)hex.charAt(2); ab[j2+5]=(byte)hex.charAt(3); k2++; j2+=6; } } return j2 - k1; } public String getCharacterEncoding() { return "Unicode"; } public int flush(byte ab[], int i, int j) { return 0; } public void reset() { } public int getMaxBytesPerChar() { return 1; } }