StringBuilderとかString#formatとか例外とか

文字列を%<16進数>形式にエスケープする。 - うなの日記のプログラムを弄ってみる。

public static final String escape(String string, String encode) {
    byte[] bs = null;
    try {
        bs = string.getBytes(encode);
    } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException(e);
    }
    StringBuilder buff = new StringBuilder(bs.length * 3);
    for (byte b : bs) {
        buff.append('%').append(String.format("%02X", b));
    }
    return buff.toString();
}

まず、buffよりbs*1を先に持ってきて、例外はチェックされないIllegalArgumentExceptionでラップして再送するように変更。
例外をどうするかって言うのは難しいんだけど、encodeはプログラマが指定するんじゃね?ってことで。


次にStringBufferをStringBuilderに。String#formatを使ってることから、StringBuilderも問題なし*2。ローカル変数だからマルチスレッドとも無関係。
あと、bsの長さの3倍が変換後の文字列の長さっていうのは分かってるから、初期容量も指定。


for文も拡張for文に。これもJ2SE5.0で追加された構文だけど、分かりやすいので良く使う。


最後、String#formatに渡す引数(第一引数)だけど、"%1$X"から"%02X"に変更。
"%1$X"は、可変長引数の最初の引数を大文字の16進数で、って言う指定で、"%02X"は0詰め、2桁の大文字の16進数で、って言う指定。微妙に違う。
まず、可変長引数が1個固定だから、参照する引数のインデックスを指定する必要はない。これで"%X"。
で、02の部分はちょっと微妙なんだけど、\rやら\nやら\tやらをオリジナルのメソッドに渡すと、

%D%A%9

となってしまう。
これが望んだ動作かどうかは分からないけど、個人的には0がついて欲しいから、0詰めを表す0と、最小幅を表す2をつけて、"%02X"。

*1:オリジナルではb

*2:どちらもJ2SE5.0で追加された