24

UTF-16 バイト配列を との間でエンコード/デコードする必要がありますjava.lang.String。バイト配列はByte Order Marker (BOM)で与えられ、BOM でバイト配列をエンコードする必要があります。

また、私は Microsoft クライアント/サーバーを扱っているので、誤解を避けるためにエンコーディングを (LE BOM と共に) リトル エンディアンで出力したいと思います。BOM を使用するとビッグ エンディアンで動作するはずですが、Windows の世界で上流に泳ぎたくはありません。

例として、BOM を使用してリトル エンディアンでjava.lang.Stringasをエンコードするメソッドを次に示します。UTF-16

public static byte[] encodeString(String message) {

    byte[] tmp = null;
    try {
        tmp = message.getBytes("UTF-16LE");
    } catch(UnsupportedEncodingException e) {
        // should not possible
        AssertionError ae =
        new AssertionError("Could not encode UTF-16LE");
        ae.initCause(e);
        throw ae;
    }

    // use brute force method to add BOM
    byte[] utf16lemessage = new byte[2 + tmp.length];
    utf16lemessage[0] = (byte)0xFF;
    utf16lemessage[1] = (byte)0xFE;
    System.arraycopy(tmp, 0,
                     utf16lemessage, 2,
                     tmp.length);
    return utf16lemessage;
}

Javaでこれを行う最良の方法は何ですか? 理想的には、最初に 2 つの余分なバイトが割り当てられた新しいバイト配列にバイト配列全体をコピーすることは避けたいと思います。

このような文字列のデコードにも同じことが言えますが、java.lang.Stringコンストラクターを使用すると、より簡単になります。

public String(byte[] bytes,
              int offset,
              int length,
              String charsetName)
4

5 に答える 5

31

「UTF-16」文字セット名は常に BOM でエンコードされ、ビッグ/リトル エンディアンを使用してデータをデコードしますが、「UnicodeBig」と「UnicodeLittle」は特定のバイト順でエンコードするのに役立ちます。BOM がない場合は UTF-16LE または UTF-16BEを使用します。「\uFEFF」を使用して BOM を手動で処理する方法については、この投稿を参照してください。文字セット文字列名または (できれば) Charsetクラスの標準的な命名については、こちらを参照してください。また、エンコードの限られたサブセットのみをサポートする必要があることにも注意してください。

于 2009-05-18T20:08:45.157 に答える
7

これはnioで行う方法です:

    return Charset.forName("UTF-16LE").encode(message)
            .put(0, (byte) 0xFF)
            .put(1, (byte) 0xFE)
            .array();

確かに高速であるはずですが、内部でいくつの配列が作成されるかはわかりませんが、API のポイントについての私の理解は、それを最小限に抑えることになっているということです。

于 2009-05-18T23:09:56.650 に答える
6

まず、デコードには文字セット「UTF-16」を使用できます。初期BOMを自動的に検出します。UTF-16BEのエンコードには、「UTF-16」文字セットを使用することもできます。これにより、適切なBOMが書き込まれ、ビッグエンディアンのものが出力されます。

BOMを使用してリトルエンディアンにエンコードする場合、(文字列が本当に巨大でない限り)二重割り当てを使用しても、現在のコードはそれほど悪くないと思います。それらがバイト配列ではなく、java.nio ByteBufferを処理し、java.nio.charset.CharsetEncoderクラスを使用する場合に実行したいことがあります。(これはCharset.forName( "UTF-16LE")。newEncoder()から取得できます)。

于 2009-05-18T20:15:47.453 に答える
3
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(string.length() * 2 + 2);
    byteArrayOutputStream.write(new byte[]{(byte)0xFF,(byte)0xFE});
    byteArrayOutputStream.write(string.getBytes("UTF-16LE"));
    return byteArrayOutputStream.toByteArray();

編集:あなたの質問を読み直すと、二重配列の割り当てを完全に避けたいと思います。残念ながら、私の知る限り、API はそれを提供しません。(方法はありましたが廃止されており、エンコーディングを指定することはできません)。

私はあなたのコメントを見る前に上記を書きました.nioクラスを使用する答えは正しい軌道に乗っていると思います. 私はそれを見ていましたが、API に精通していないので、それをどのように行うかをすぐに知ることができません。

于 2009-05-18T20:09:49.897 に答える