爪哇:String 与 ByteBuffer 之间的转换及相关问题
-
12-09-2019 - |
题
我使用 Java NIO 进行套接字连接,并且我的协议是基于文本的,因此我需要能够在将字符串写入 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;
}
这在大多数情况下都有效,但我怀疑这是否是执行此转换的每个方向的首选(或最简单)方法,或者是否有其他方法可以尝试。有时,似乎是随机的,打电话给 encode()
和 decode()
会抛出一个java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END
异常或类似的情况,即使我每次完成转换时都使用新的 ByteBuffer 对象。我需要同步这些方法吗?有更好的方法在字符串和字节缓冲区之间进行转换吗?谢谢!
解决方案
查看 CharsetEncoder
和 CharsetDecoder
API 描述 - 您应该遵循 方法调用的具体顺序 以避免这个问题。例如,对于 CharsetEncoder
:
- 通过重置编码器
reset
方法,除非以前没有使用过; - 调用
encode
方法零次或多次,只要附加输入可用,传递false
对于 endOfInput 参数,填充输入缓冲区并在调用之间刷新输出缓冲区; - 调用
encode
方法一最后一次,通过true
对于 endOfInput 参数;进而 - 调用
flush
方法,以便编码器可以将任何内部状态刷新到输出缓冲区。
顺便说一句,这与我在 NIO 中使用的方法相同,尽管我的一些同事知道他们只使用 ASCII,将每个字符直接转换为字节,我可以想象这可能更快。
其他提示
除非事情已经改变了,你有更好
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()将或者总是为真或取决于你的使用情况始终为false。在实践中,除非你真的希望它在任何情况下工作,它是安全的优化掉你不需要的分支。
Adamski 的回答很好,描述了使用通用编码方法(将字节缓冲区作为输入之一)时编码操作的步骤
然而,所讨论的方法(在本次讨论中)是编码的一种变体 - 编码(CharBuffer 中). 。这是一个 实现整个编码操作的便捷方法. 。(请参阅 P.S. 中的 java 文档参考)
根据文档, 因此,如果编码操作已经在进行中,则不应调用此方法 (这就是 ZenBlender 代码中发生的情况——在多线程环境中使用静态编码器/解码器)。
就我个人而言,我喜欢使用 方便 方法(相对于更通用的编码/解码方法),因为它们通过在幕后执行所有步骤来减轻负担。
ZenBlender 和 Adamski 已经在他们的评论中提出了多种安全执行此操作的方法选项。在这里将它们全部列出:
- 在每个操作需要时创建一个新的编码器/解码器对象(效率不高,因为它可能会导致大量对象)。或者,
- 使用 ThreadLocal 可以避免为每个操作创建新的编码器/解码器。或者,
- 同步整个编码/解码操作(这可能不是首选,除非牺牲一些并发性对您的程序来说是可以的)
附:
java 文档参考: