我正在使用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之间转换的更好方法是什么?谢谢!
答案 0 :(得分:51)
查看CharsetEncoder
和CharsetDecoder
API说明 - 您应该按照特定的方法调用序列来避免此问题。例如,对于CharsetEncoder
:
reset
方法重置编码器,除非之前未使用过; encode
方法零次或多次,只要有其他输入可用,传递false
为endOfInput参数并填充输入缓冲区并在调用之间刷新输出缓冲区; encode
方法,将true
传递给endOfInput参数;然后flush
方法,以便编码器可以将任何内部状态刷新到输出缓冲区。顺便说一句,这与我用于NIO的方法相同,尽管我的一些同事正在将每个字符串直接转换为知识中的一个字节,他们只使用ASCII,我可以想象它可能更快。
答案 1 :(得分:27)
除非事情发生变化,否则最好还是
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,具体取决于您的用例。在实践中,除非您真的希望它在任何情况下都可以工作,否则可以安全地优化您不需要的分支。
答案 2 :(得分:14)
Adamski的答案很好,并描述了使用通用编码方法(以字节缓冲区作为输入之一)时编码操作的步骤。
然而,有问题的方法(在本讨论中)是编码的变体 - 编码(CharBuffer in)。这是一种实现整个编码操作的便捷方法。 (请参阅P.S中的java docs参考资料)
根据文档,因此,如果编码操作已在进行中,则不应调用此方法(这是ZenBlender代码中发生的事情 - 在多线程中使用静态编码器/解码器环境)。
就个人而言,我喜欢使用便利方法(通过更通用的编码/解码方法),因为它们通过执行所有步骤来消除负担。
ZenBlender和Adamski已经提出了多种方法可以在评论中安全地做到这一点。在这里列出所有内容:
P.S。
java docs references: