Java:将string转换为ByteBuffer以及相关的问题
我为我的套接字连接使用Java NIO,并且我的协议是基于文本的,所以我需要能够将string转换为ByteBuffers,然后将它们写入到SocketChannel中,然后将传入的ByteBuffers转换回string。 目前,我正在使用这个代码:
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
exception,或类似的,即使我每次使用一个新的ByteBuffer对象转换完成。 我需要同步这些方法吗? 任何更好的方法来转换string和ByteBuffers? 谢谢!
查看CharsetEncoder
和CharsetDecoder
API描述 – 您应该按照特定的方法调用顺序来避免这个问题。 例如,对于CharsetEncoder
:
- 除非之前没有使用,否则通过
reset
方法重置编码器; - 只要额外的input可用,调用
encode
方法零次或多次,为endOfInputparameter passingfalse
并填充input缓冲区并在调用之间清空输出缓冲区; - 最后一次调用
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()将始终为true或始终为false。 在实践中,除非您真的希望在任何情况下都能正常工作,否则优化您不需要的分支是安全的。
通过回答Adamski是一个很好的一个,并介绍了在使用通用编码方法时的编码操作中的步骤(将字节缓冲区作为其中一个input)
然而,所讨论的方法(在这个讨论中)是一种编码 – 编码(CharBuffer in)的变体。 这是一个实现整个编码操作的简便方法 。 (请参阅PS中的java docs参考)
根据文档, 如果编码操作已经在进行中 (这是ZenBlender的代码中发生了什么 – 在multithreading环境中使用静态编码器/解码器), 则不应调用此方法 。
就个人而言,我喜欢使用便捷的方法(通过更一般的编码/解码方法),因为它们通过执行所有步骤来减轻负担。
ZenBlender和Adamski已经提出了多种方法来在他们的评论中安全地做到这一点。 在这里列出它们全部:
- 每个操作需要时创build一个新的编码器/解码器对象(效率不高,因为它可能导致大量的对象)。 要么,
- 使用ThreadLocal可避免为每个操作创build新的编码器/解码器。 要么,
- 同步整个编码/解码操作(这可能不是首选,除非牺牲一些并发性适用于您的程序)
PS
java文档参考:
- 编码(方便)方法: http : //docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
- 常规编码方法: http : //docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java.nio.ByteBuffer,%20boolean% 29