87

ソケット接続にJavaNIOを使用しており、プロトコルはテキストベースであるため、SocketChannelに書き込む前に文字列をByteBufferに変換し、着信ByteBufferを文字列に戻す必要があります。現在、私はこのコードを使用しています:

public static Charset charset = Charset.forName("UTF-8");
public static CharsetEncoder encoder = charset.newEncoder();
public static CharsetDecoder decoder = charset.newDecoder();

public static ByteBuffer str_to_bb(String msg){
  try{
    return encoder.encode(CharBuffer.wrap(msg));
  }catch(Exception e){e.printStackTrace();}
  return null;
}

public static String bb_to_str(ByteBuffer buffer){
  String data = "";
  try{
    int old_position = buffer.position();
    data = decoder.decode(buffer).toString();
    // reset buffer's position to its original so it is not altered:
    buffer.position(old_position);  
  }catch (Exception e){
    e.printStackTrace();
    return "";
  }
  return data;
}

これはほとんどの場合機能しますが、これがこの変換の各方向を実行するための好ましい(または最も簡単な)方法であるかどうか、または別の方法で試すことができるかどうか疑問に思います。変換が行われるたびに新しいByteBufferオブジェクトを使用している場合でも、ときどき、一見ランダムに、例外などを呼び出してencode()スローdecode()し ます。java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_ENDこれらのメソッドを同期する必要がありますか?文字列とByteBufferの間で変換するためのより良い方法はありますか?ありがとう!

4

3 に答える 3

54

CharsetEncoderおよびCharsetDecoderAPI の説明を確認してください。この問題を回避するには、メソッド呼び出しの特定のシーケンスに従う必要があります。たとえば、次の場合CharsetEncoder:

  1. reset以前に使用されていない限り、メソッドを介してエンコーダーをリセットします。
  2. encode追加の入力が利用可能である限り、メソッドを 0 回以上false呼び出し、endOfInput 引数を渡し、呼び出しの間に入力バッファーを満たし、出力バッファーをフラッシュします。
  3. endOfInput 引数encodeを渡して、最後にもう一度メソッドを呼び出します。trueその後
  4. flushエンコーダーが内部状態を出力バッファーにフラッシュできるように、メソッドを呼び出します。

ところで、これは私が NIO に使用しているのと同じアプローチですが、私の同僚の何人かは、ASCII のみを使用しているという知識で各文字を直接バイトに変換していますが、おそらく高速であると想像できます。

于 2009-08-09T23:35:07.890 に答える
44

状況が変わっていない限り、あなたはより良い

public static ByteBuffer str_to_bb(String msg, Charset charset){
    return ByteBuffer.wrap(msg.getBytes(charset));
}

public static String bb_to_str(ByteBuffer buffer, Charset charset){
    byte[] bytes;
    if(buffer.hasArray()) {
        bytes = buffer.array();
    } else {
        bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
    }
    return new String(bytes, charset);
}

通常、 buffer.hasArray() は、ユース ケースに応じて、常に true または常に false になります。実際には、どのような状況でも本当に動​​作させたい場合を除き、不要なブランチを最適化しても安全です。

于 2015-06-15T22:45:03.837 に答える
14

Adamskiによる回答は良いものであり、一般的なエンコード方法(入力の1つとしてバイトバッファーを使用する)を使用する場合のエンコード操作の手順を説明しています。

ただし、問題のメソッド(この説明では)はencode --encode(CharBuffer in)の変形です。これは、エンコーディング操作全体を実装する便利な方法です。(PSのJavaドキュメントリファレンスを参照してください)

したがって、ドキュメントによると、エンコード操作がすでに進行中の場合は、このメソッドを呼び出さないでください(これは、ZenBlenderのコードで発生していることです-マルチスレッド環境で静的エンコーダー/デコーダーを使用します)。

個人的には、(より一般的なエンコード/デコード方法よりも)便利な方法を使用するのが好きです。これは、すべての手順を内部で実行することで負担を取り除くためです。

ZenBlenderとAdamskiは、コメントでこれを安全に行うための複数の方法のオプションをすでに提案しています。それらすべてをここにリストします:

  • 操作ごとに必要に応じて、新しいエンコーダー/デコーダーオブジェクトを作成します(オブジェクトの数が多くなる可能性があるため、効率的ではありません)。また、
  • ThreadLocalを使用して、操作ごとに新しいエンコーダー/デコーダーが作成されないようにします。また、
  • エンコード/デコード操作全体を同期します(プログラムで並行性をいくらか犠牲にしても問題ない場合を除いて、これは好ましくない場合があります)

PS

javaドキュメントの参照:

  1. エンコード(コンビニエンス)メソッド:http ://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
  2. 一般的なエンコード方法:http ://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java.nio.ByteBuffer,%20boolean% 29
于 2012-09-26T02:58:20.170 に答える