2009-05-18 9 views
20

java.lang.Stringとの間でUTF-16バイト配列をエンコード/デコードする必要があります。バイト配列はByte Order Marker (BOM)で私に与えられ、BOMを使ってバイト配列をエンコードする必要があります。UTF-16LEバイト配列をBOMでエンコード/デコードするにはどうすればよいですか?

また、私はMicrosoftクライアント/サーバーを扱っているため、誤解を避けるためにリトルエンディアン(LE BOMと一緒に)でエンコーディングしたいと思います。私は、BOMでビッグエンディアンが動作するはずですが、Windowsの世界で上流に泳ぎたいとは思わないことを認識しています。一例として、

、ここでBOMとリトルエンディアンでUTF-16ようjava.lang.Stringをエンコードする方法は次のとおりです。

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 constructor

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

答えて

27

「UTF-16」文字セット名は常にBOMでエンコードし、いずれかを使用してデータをデコードしますbig/little endiannessですが、 "UnicodeBig"と "UnicodeLittle"は特定のバイト順でエンコーディングするのに便利です。 「\ uFEFF」を使用してBOMを手動で処理する方法については、BOMなしの場合はUTF-16LEまたはUTF-16BEを使用してください。see this post文字セット文字列名の正規名または(好ましくは)Charsetクラスについては、hereを参照してください。 limited subset of encodingsだけがサポートされることが絶対必要であることにも注意してください。

+1

ありがとう:

は、ここで私がなってしまったものです!しかし、もう1つの問題は...「UTF-16」を使用するとBig Endianとしてデータがエンコードされますが、BOMが存在するにもかかわらず、Microsoftのデータではうまくいかないと思われます。 JavaでBOMを使ってUTF-16LEをエンコードする方法はありますか?私は本当に探していたものを反映するために私の質問を更新します... –

+0

彼が与えた "この記事を見る"リンクをクリックしてください。基本的には、文字列の先頭に\ uFEFF文字を埋め込み、次にUTF-16LEにエンコードすると、結果は適切なBOMになります。 –

+0

"UnicodeLittle"を使用してください(JREがサポートしていると仮定します - そうでなければ "\ uEFFF" + "私の文字列")getBytes( "UTF-16LE")。 Microsoft APIがBOMを期待していてもビッグエンディアンのデータを処理できないと驚いていますが、BOMを他のプラットフォームよりも使用する傾向があります。空の文字列でテストする - データがない場合は、空の配列を取得することがあります。 – McDowell

2
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(); 

EDIT:二重配列の割り当てを避けることをお勧めします。残念ながら、私が知る限り、APIはそれをあなたに与えません。 (メソッドがありましたが、非推奨であり、エンコードを指定することはできません)。

私はあなたのコメントを見た前に私は、私は正しいトラックには、nioクラスを使用する答えがあると思います。私はそれを見ていましたが、私はあなたがそれをどうやって手に入れたかを知るのに十分なAPIに慣れていません。

+0

ありがとうございます。さらに私がここで気に入っていたのは、string.getBytes( "UTF-16LE")でバイト配列全体を割り当てないことです。おそらくストリームをInputStreamとしてラップすることで、これは私の以前の質問のポイントでした。 /stackoverflow.com/questions/837703/how-can-i-get-a-java-io-inputstream-from-a-java-lang-string –

+0

このコードは実際にはStringに対して十分大きな配列を3回割り当て、あなたが.toByteArray()の呼び出しでコピーされたByteArrayOutputStreamの内部配列を持っているからです。 2つだけを割り当てることに戻す方法は、ByteArrayOutputStreamをOutputStreamWriterにラップし、それに文字列を書き込むことです。それから、ByteArrayOutputStreamの内部状態と.toByteArray()によって作られたコピーが残っていますが、.getBytesの戻り値ではありません。 –

+0

OutputStreamWriterデリゲートとして、バイト配列のchar配列を交換しているようですStreamEncoderクラスに渡します。このクラスは、char []バッファを作成してStringデータを取得します。文字列は不変であり、配列のサイズは不変であるため、コピーは避けられないようです。私はニオがByteArrayOutputStreamのダブル作成を手助けすると考えていると思います – Yishai

6

まず、デコードのために、文字セット "UTF-16"を使用できます。初期BOMを自動的に検出します。 UTF-16BEをエンコードするには、適切なBOMを作成してビッグエンディアンを出力する "UTF-16"文字セットを使用することもできます。

リトルエンディアンをBOMでエンコードする場合、文字列が本当に怪物でない限り、ダブルアロケーションでも現在のコードが悪いとは思われません。もしそうであれば、バイト配列を処理するのではなく、java.nio ByteBufferを処理し、java.nio.charset.CharsetEncoderクラスを使用します。 (あなたはCharset.forName( "UTF-16LE")から取得できます。newEncoder())。

+0

ありがとう、良いアドバイス。 –

7

これは、あなたがNIOでそれを行う方法です。

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

確かに速いことになっているが、私はそれがカバーの下になりますどのように多くのアレイを知りませんが、ポイントの私の理解APIはそれを最小化することになっているということです。

+0

これは実際には動作しません。 put(0)とput(1)の呼び出しは、エンコードされたメッセージのByteBufferの最初の2バイトを上書きします。 – hopia

0

これは古い質問ですが、依然として私の状況に受け入れられる回答は見つかりませんでした。基本的に、Javaには、BOMを持つUTF-16LE用の組み込みエンコーダーはありません。したがって、独自の実装を展開する必要があります。

private byte[] encodeUTF16LEWithBOM(final String s) { 
    ByteBuffer content = Charset.forName("UTF-16LE").encode(s); 
    byte[] bom = { (byte) 0xff, (byte) 0xfe }; 
    return ByteBuffer.allocate(content.capacity() + bom.length).put(bom).put(content).array(); 
} 
関連する問題