首页 文章

Java:将字符串转换为ByteBuffer和来自ByteBuffer的字符串以及相关问题

提问于
浏览
76

我使用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_END 异常或类似异常,即使我每次转换完成时都使用新的ByteBuffer对象 . 我需要同步这些方法吗?在字符串和ByteBuffers之间转换的更好方法是什么?谢谢!

3 回答

  • 23

    除非事情发生变化,否则你会更好

    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,具体取决于您的用例 . 在实践中,除非您真的希望它在任何情况下都能工作,否则可以安全地优化掉您不需要的分支 .

  • 13

    查看CharsetEncoderCharsetDecoder API说明 - 您应该按照 specific sequence of method calls 来避免此问题 . 例如,对于 CharsetEncoder

    • 通过 reset 方法重置编码器,除非之前没有使用过;

    • 只要有额外的输入可用,调用 encode 方法零次或多次,为endOfInput参数传递 false 并填充输入缓冲区并在调用之间刷新输出缓冲区;

    • 最后一次调用 encode 方法,为endOfInput参数传递 true ;然后

    • 调用 flush 方法,以便编码器可以将任何内部状态刷新到输出缓冲区 .

    顺便说一句,这与我用于NIO的方法相同,尽管我的一些同事正在将每个字符串直接转换为知识中的一个字节,他们只使用ASCII,我可以想象它可能更快 .

  • 50

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

    但是,有问题的方法(在本讨论中)是编码的变体 - encode(CharBuffer in) . 这是 convenience method that implements the entire encoding operation . (请参阅P.S中的java docs参考资料)

    根据文档, This method should therefore not be invoked if an encoding operation is already in progress (这是ZenBlender代码中发生的事情 - 在多线程环境中使用静态编码器/解码器) .

    就个人而言,我喜欢使用便利方法(通过更通用的编码/解码方法),因为它们通过执行所有步骤来消除负担 .

    ZenBlender和Adamski已经提出了多种方法可以在评论中安全地做到这一点 . 在这里列出所有:

    • 在每个操作需要时创建一个新的编码器/解码器对象(效率不高,因为它可能导致大量对象) . 要么,

    • 使用ThreadLocal可避免为每个操作创建新的编码器/解码器 . 要么,

    • 同步整个编码/解码操作(除非牺牲一些并发性,否则这可能不是首选)

    附:

    java docs参考:

相关问题