Java:将字符串与ByteBuffer进行转换以及相关问题

Java:将字符串与ByteBuffer进行转换以及相关问题,java,string,character-encoding,nio,bytebuffer,Java,String,Character Encoding,Nio,Bytebuffer,我使用Java NIO进行套接字连接,并且我的协议基于文本,因此我需要能够在将字符串写入SocketChannel之前将其转换为ByteBuffers,并将传入的ByteBuffers转换回字符串。目前,我正在使用以下代码: public static Charset charset = Charset.forName("UTF-8"); public static CharsetEncoder encoder = charset.newEncoder(); public static Char

我使用Java NIO进行套接字连接,并且我的协议基于文本,因此我需要能够在将字符串写入SocketChannel之前将其转换为ByteBuffers,并将传入的ByteBuffers转换回字符串。目前,我正在使用以下代码:

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;
}
这在大多数情况下都是有效的,但我怀疑这是否是实现这种转换的每个方向的首选(或最简单)方法,或者是否有其他方法可以尝试。偶尔调用
encode()
decode()
会抛出
java.lang.IllegalStateException:Current state=FLUSHED,new state=CODING\u END
exception或类似,即使每次转换完成时我都使用新的ByteBuffer对象。我需要同步这些方法吗?有更好的方法在字符串和ByteBuffers之间转换吗?谢谢

查看和API说明-您应该遵循方法调用的特定顺序来避免此问题。例如,对于
CharsetEncoder

  • 通过
    Reset
    方法重置编码器,除非之前未使用过
  • 调用
    encode
    方法零次或多次,只要有额外的输入可用,为endOfInput参数传递
    false
    ,并在调用之间填充输入缓冲区和刷新输出缓冲区
  • 最后一次调用
    encode
    方法,为endOfInput参数传递
    true
    ;然后
  • 调用
    flush
    方法,以便编码器可以将任何内部状态刷新到输出缓冲区

  • 顺便说一句,这与我在NIO中使用的方法相同,尽管我的一些同事仅使用ASCII将每个字符直接转换为一个字节,我可以想象这可能更快。

    Adamski的回答很好,并描述了使用通用编码方法时编码操作的步骤(将字节缓冲区作为输入之一)

    但是,所讨论的方法(在本讨论中)是encode-encode(CharBuffer in)的一种变体。这是一种实现整个编码操作的方便方法(请参见第页中的java文档参考)

    根据文档,因此,如果编码操作已经在进行中,则不应调用此方法(这就是ZenBlender代码中发生的情况——在多线程环境中使用静态编码器/解码器)

    就个人而言,我喜欢使用方便的方法(而不是更一般的编码/解码方法),因为它们通过在幕后执行所有步骤来减轻负担

    ZenBlender和Adamski已经在他们的评论中提出了多种安全的方法选项。在这里列出了所有这些方法:

    • 在每次操作需要时创建一个新的编码器/解码器对象(效率不高,因为它可能会导致大量对象)。或者
    • 使用ThreadLocal避免为每个操作创建新的编码器/解码器。或者
    • 同步整个编码/解码操作(这可能不是首选,除非牺牲一些并发性对您的程序来说是可以的)
    附言

    java文档参考:

  • 编码(方便)方法:
  • 通用编码方法:

  • 除非情况有所改变,否则你最好还是

    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()根据您的用例,将始终为真或始终为假。实际上,除非您真的希望它在任何情况下都能工作,否则优化掉您不需要的分支是安全的。

    查看异常的完整堆栈跟踪会有所帮助。非常感谢,这非常有帮助!我发现我确实有多个线程c同时调用我的转换函数,尽管我没有将其设计为允许这样做。我通过调用charset.newEncoder().encode()和charset.newDecoder().decode()修复了它为了确保每次都使用一个新的编码器/解码器以避免并发问题,或者不必要地在那些对象上进行同步,这些对象在我的情况下不共享有意义的数据。我还运行了一些测试,发现使用newEncoder()/newDecoder()没有可测量的性能差异每次!没问题。您可以避免每次都创建新的编码器/解码器,但仍然可以通过使用ThreadLocal保持线程安全,并根据需要为每个线程懒洋洋地创建一个专用编码器/解码器(这就是我所做的)。这行得通吗?新字符串(bb.array(),0,bb.array().length,“UTF-8”)