|
C 言語スタイルのフォーマット Formatter |
||||
|
||||
C 言語を使われている方ならば、printf の恩恵は少なからず受けているはずです。printf を使ったフォーマットはとても楽チンにもかかわらず、かなり強力です。 Java で printf のようなフォーマットを行おうとしたら、java.text.MessageFormat クラスを使用するしかありません。しかし、このクラスが使いやすいとはお世辞にもいえません。Tiger になって可変長引数が使えるようになったので、少しは改善しましたが、やっぱり使いにくいことは変わりません。 やっぱり printf だよねということで、JSR-51 の New I/O では C 言語の printf/scanf 相当のフォーマッティングを扱えることが目的の 1 つにあげられていました。 ところが、New I/O が導入された J2SE 1.4 Merline ではこの項目だけペンディングになってしまったのです。そして、やっと Tiger で取り入れられることになりました。 しかし、まだ油断はできません。これについては 2003 年の JavaOne の BOF で発表されているのですが、その内容と J2SE 1.5 での内容はかなり食い違いがあります。それだけでなく、alpha, beta1, beta2 の間でもずいぶん変わってきているのです。 早くフィックスしてくれないかなぁ。
|
|
|||||||
さっそく使ってみましょう。別に難しいことはいりません。単に printf メソッドを実行するだけです。
printf というメソッドが追加されたのですが、同じように format メソッドも追加されているので、これも使っています。
実行してみましょう。
System.out は java.io.PrintStream クラスなので、このクラスの printf メソッドの定義を見てみましょう。printf メソッドは内部で format メソッドを呼び出しています。
重要なのは赤い字のところです。format メソッドは Formatter オブジェクトを生成して、その format メソッドをコールしていることが分かります。 この java.util.Formatter クラスが C 言語の printf 相当のフォーマットを行う立役者なのです。
|
|
|||||
上記のコードでは Formatter クラスのインスタンス生成には PrintStream オブジェクトを引数にしていました。JavaDoc を見てみると、フォーマットした出力先を引数にできるようです。その出力先として、Appendable インタフェース、File クラス、OutputStream クラス、ファイル名があります。 そのほかにロケールや文字コードも指定できます。 何も引数がない場合や、Appendable インスタンスが null の時には StringBuilder オブジェクトを生成して使用するようです。 生成してしまったら、後は printf のように使うだけです。ただし、メソッド名は printf ではなくて format です。
Appendable インタフェースをインプリメントしているクラスとしてここでは StringBuilder クラスを使用しました。
PrintStream クラスなどのように出力先があればいいのですが、StringBuilder クラスにはないので out メソッドを用いて、StringBuilder オブジェクトを取り出し、それを出力しています。 Formatter クラスには toString メソッドも定義されているので、System.out.println(formatter); としても同じ結果が得られます。
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Formatter クラスが使えるフォーマット記述子は基本的には C の printf と同じですが、拡張も行われています。拡張された部分としては時間に関するフォーマットと引数の順番を指定するものなどがあります。 フォーマットは次のように記述します。
argument_index というのが拡張された部分です。たとえば %1$d と書けば、1 番目の引数ということになります。サンプルを見てもらうのが一番手っ取り早いですね。
argument_index を使用することで引数の順番と、表示の順番を変えることができます。また、繰り返し使うこともできます。
実行すると次のようになります。
flag 以下の書き方は C の printf と同じです。 変換記述子は次に示すカテゴリに分けられています。
それぞれ、次のような文字を使用することができます。大文字と小文字は区別されます。
習うより慣れろというわけで、あまりなじみのない時間に関するものを集めたサンプルを作ってみました。
このプログラムはロケール依存なので、デフォルトロケール(ja_JP) とアメリカのロケール (en_US) で実行してみました。左が日本、右がアメリカです。
次にフラグです。フラグに使用されるのは次の 6 種類です。
これらのフラグはカテゴリによって適用できるものとできないものがあります。
複数のフラグを一緒に使うこともできます。符号付で左詰だったら -+ と表記できます。 フラグの使い方も習うより慣れろで、やってみましょう。
実行してみれば、すぐにどういう出力になるか理解できるので、一度はやってみましょう ^^;;
Formatter クラスの JavaDoc にはここに記したフラグだけしか記述されていないのですが、フラグをあらわす FormattableFlags クラスにはもう 1 つ '^' というフラグが書いてあります。 このフラグはすべて大文字で表記するかを示しているようなのですが、実際にフラグとして使用すると UnknownFormatConversionException 例外が発生してしまいます。 どうなってるんでしょ?
|
|
||||
前述した FormatSample クラスでは PrintStream クラスの format メソッドを使用しました。 このクラス以外に PrintWriter クラス、String クラスが内部的に Format クラスを使用しています。PrintWriter クラスは PrintStream クラスと同じ使い方でいいのですが、String クラスはちょっとだけ違っています。 String クラスの format メソッドは戻り値が String になります。ちょうど、C 言語の sprintf のような感じです。 String#format メソッドの定義は次のようになっています。
デフォルトコンストラクタで Formatter オブジェクトを生成しているので、StringBuilder クラスを使用したものと同じです。 String クラスを使うもよし、Formatter クラスを直接使うのもよし、ぐらいの感覚ですね。
|
|
||||||||||||||||||||
せっかくなので、自作したクラスも Formatter クラスでフォーマットできるようにしてみましょう。 自作したクラスはカテゴリとしては一般になります。とすると b と h は動作が決まってしまっているので、s の時にどのようにフォーマットすればいいかを考えることになります。 自作したクラスでフォーマットできるようにするには java.util.Formattable インタフェースをインプリメントします。Formattable インタフェースでは formatTo メソッドが定義されており、ここにフォーマットのための処理を記述します。 public void formatTo(Formatter formatter, int flags, int width, int precision) 第 2 引数の flags は java.util.FormattableFlags クラスで定義されている以下の 3 つの値を使用します。
これらの値は OR をとることができます。 width と precision は指定されていないときには -1 になります。 それでは Formattable インタフェースをインプリメントしたサンプルを作ってみましょう。
単に名前を保持する Name というクラスを作ってそれをフォーマットできるようにしました。日本語の処理もいれると複雑になってしまうので英語だけに対応しています。
formatTo メソッドはフラグと幅を考えなくてはいけないのでロジックはちと面倒になります。precision はここでは省略しています。 次のようなルールでフォーマットするようにしました。
width は表示幅が指定されていないときは -1 なので、先にそれを調べてしまいます。表示幅が指定されている場合、その幅に応じてラストネームだけにするかフルネームにするかなどを決めています。 左詰と大文字化は、表示幅のロジックとは独立してできるので if 分は別にできます。 フォーマットを試す部分は次のようにしてみました。
これを実行してみると、次のようになります。
|
|
||||
printf が使えるということはこんなにも楽だったのか、とあらためて気がつきました。それも C よりも機能が多いのですぐにでも使えそうです。 また、Formatter クラスを意識しなくても String クラスや PrintWriter/PrintStream クラスですぐ使えるのもポイントが高いですね。 もう 1 つの scanf については、項を改めて解説したいと思います。
今回使用したサンプルはここからダウンロードできます。
(Jun. 2004) |
|