Java ByteBuffer 清除数据

标签 java nio stringbuilder bytebuffer

我知道 Java 的 ByteBuffer.clear() 并不是真的要清除 ByteBuffer 中的所有数据,所以当我每次使用 StringBuilder.append() 字符串时,最后的结果总是附加所有剩余的字符在 ByteBuffer 中,这是上次写入的旧数据,那么如何解决这个问题?

int byteRead = -1;
int readCount = 0;
int BUFFER_SIZE = 256;
StringBuilder sb = new StringBuilder();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
ReadableByteChannel readableByteChannel = Channels.newChannel(is);
while ((byteRead = readableByteChannel.read(buffer)) > 0 && readCount < 68) {
    sb.append(new String(buffer.array(), "UTF-8"));
    buffer.clear();
    readCount++;
}

最佳答案

正如其他答案已经指出的那样,您必须考虑缓冲区的位置,缓冲区的位置由 read 方法更新。所以正确的代码看起来像:

while ((byteRead = readableByteChannel.read(buffer)) > 0 && readCount < 68) {
    sb.append(new String(buffer.array(),
        buffer.arrayOffset(), buffer.arrayOffset()+buffer.position(), "UTF-8"));
    buffer.clear();
    readCount++;
}

请注意,在您的特殊情况下,arrayOffset() 将始终为零,但您最好以某种方式编写代码,当您在缓冲区分配代码中更改某些内容时它不会中断.

但是这段代码被破坏了。当您读取多字节 UTF-8 序列时,可能会发生这种情况,该序列的第一个字节在一个操作中被读取,而其余字节在下一个操作中被读取。您尝试从这些不完整的序列创建 String 实例将产生无效字符。除此之外,您正在创建这些 String 实例,只是为了将它们的内容复制到 StringBuilder,这是非常低效的。

因此,要正确执行此操作,您应该执行以下操作:

int readCount = 0;
int BUFFER_SIZE = 256;
StringBuilder sb = new StringBuilder();
CharsetDecoder dec=StandardCharsets.UTF_8.newDecoder();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
CharBuffer cBuffer= CharBuffer.allocate(BUFFER_SIZE);
ReadableByteChannel readableByteChannel = Channels.newChannel(is);
while(readableByteChannel.read(buffer) > 0 && readCount < 68) {
    buffer.flip();
    while(dec.decode(buffer, cBuffer, false).isOverflow()) {
        cBuffer.flip();
        sb.append(cBuffer);
        cBuffer.clear();
    }
    buffer.compact();
    readCount++;
}
buffer.flip();
for(boolean more=true; more; ) {
    more=dec.decode(buffer, cBuffer, true).isOverflow();
    cBuffer.flip();
    sb.append(cBuffer);
    cBuffer.clear();
}

请注意,ReadableByteChannelCharsetDecoder 如何使用它们的位置和限制来处理缓冲区。您所要做的就是正确使用 flipcompact 作为 shown in the documentation of compact .

唯一的异常(exception)是附加到 Stringbuilder,因为那不是 NIO 函数。在那里,我们必须使用 clear(),因为我们知道 Stringbuilder.append 操作会消耗缓冲区中的所有字符。

请注意,这段代码仍然没有处理某些(不可避免的)错误情况,因为您在任意数量的 read 之后停止,所以您总是有可能在多个字节 UTF-8 序列。


但是这个相当复杂的逻辑已经由 JRE 实现了,如果你放弃在一定数量的 bytes 之后切割的想法,你可以利用它:

int readCount = 0;
int BUFFER_SIZE = 256;
StringBuilder sb = new StringBuilder();
CharBuffer cBuffer= CharBuffer.allocate(BUFFER_SIZE);
ReadableByteChannel readableByteChannel = Channels.newChannel(is);
Reader reader=Channels.newReader(readableByteChannel, "UTF-8");
while(reader.read(cBuffer) > 0 && readCount < 68) {
    cBuffer.flip();
    sb.append(cBuffer);
    cBuffer.clear();
    readCount++;
}

现在这段代码会将读取限制为 256 × 68 字符 而不是字节,但是对于 UTF-8 编码的数据,这使得仅当存在多字节序列时才会有所不同,而您之前显然并不关心这些序列。

最后,因为您显然首先有一个 InputStream,所以您根本不需要 ReadableByteChannel 迂回:

int readCount = 0;
int BUFFER_SIZE = 256;
StringBuilder sb = new StringBuilder();
CharBuffer cBuffer = CharBuffer.allocate(BUFFER_SIZE);
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8);
while(reader.read(cBuffer) > 0 && readCount < 68) {
    cBuffer.flip();
    sb.append(cBuffer);
    cBuffer.clear();
    readCount++;
}

这可能看起来“不是 NIO 代码”,但是 Reader 仍然是读取字符数据的规范方式,即使是使用 NIO;没有替代品。 method Reader.read(CharBuffer)在 NIO 的第一个版本中缺失,但随 Java 5 一起提交。

关于Java ByteBuffer 清除数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37787987/

相关文章:

Java/Android - 快速 ByteBuffer 解析

c# - 未捕获的语法错误 : Unexpected identifier in StringBuilder

java - 在Java中携带这些变量有什么问题吗?

java - Spring Data Neo4j 4.x 和 sdn-大学 : Neo4jTemplate no autowire'ing

java - RabbitMQ 批量确认

C#:将字符串插入另一个字符串 - 性能问题

asp.net - 关于字符串生成器

java - 亚马逊云数据库 : Could not unconvert attribute error

java - netty分配的direct buffer的内存在哪里,内核空间还是用户空间?

android - 如何使用 Android NDK 将整数颜色的像素数组绑定(bind)到纹理?