SeekableByteChannel 俄语字符

SeekableByteChannel russian chars

最近我开始学习java.nio。我在教科书中有一个例子,如何使用 SeekableByteChannel:

读取文本文件
int count;
Path path;

try {
    path = Paths.get(System.getProperty("user.home") + "/desktop/text.txt");
} catch (InvalidPathException e) {
    out.println(e.getMessage());
    return;
}

try (SeekableByteChannel channel = Files.newByteChannel(path)) {
    ByteBuffer buffer = ByteBuffer.allocate(128);

    do {
        count = channel.read(buffer);

        if (count != -1) {
            buffer.rewind();
            for (int i = 0; i < count; i++)
                out.print((char) buffer.get());
        }
    } while (count != -1);

} catch (IOException e) {
    out.println("File not found!!!");
}

out.flush();

所以我使用 ANSI 编码制作了一个包含英语和俄语单词的文本文件。这就是我得到的:

方法buffer.get()returnsbyte值和俄文字符从1000开始。所以我已将编码更改为 UTF-8 并使用另一种方法:

for (int i = 0; i < count; i += 2)
    out.print(buffer.getChar()); //reads 2 bytes and converts them to char

但这给了我一行问号。

那么有人知道如何使用 SeekableByteChannel 正确阅读俄语文本吗?

ByteBuffer的方法getChar()读取两个字节,并解释为char的高字节和低字节,也就是说,总是使用UTF-16编码。一般来说,你不应该尝试手动将字节拼凑成 Strings,旧的 I/O API 和 NIO 都不行。仅提及在尝试手动解码缓冲区中的字节时必须处理的一件事,即缓冲区中的字节可能不会在多字节编码的字符边界处结束。

如果您想从 SeekableByteChannel 中读取文本,您可以使用 Channels.newReader(…) 构造一个 Reader 使用指定的字符集来解码字节。

但是,当然,您可以完全跳过 Channel 内容并使用 Files.newBufferedReader(…)Path.

创建一个 Reader

顺便说一句,示例代码是有问题的,即使是读取字节序列也是如此。这是一个简化的例子:

Path path=Paths.get(System.getProperty("user.home")).resolve("desktop/text.txt");
try(FileChannel channel=FileChannel.open(path)) {
  ByteBuffer buffer = ByteBuffer.allocate(128);
  while(channel.read(buffer)!=-1) {
    buffer.flip();
    while(buffer.hasRemaining())
        System.out.printf("%02x ", buffer.get());
    buffer.clear();
    System.out.println();
  }
} catch (IOException e) {
    System.out.println(e.toString());
}

A ByteBuffer 知道它包含多少字节(即已通过读取操作放入其中)。使用 flip 可以准备缓冲区以读出它们,例如使用示例中的循环或写入另一个通道。当你知道你已经处理完所有内容时,你可以使用 clear 将缓冲区设置为初始状态,可以从头到尾填充它。

否则,如果它可能包含未处理的数据,请改用compact,这会将未处理的数据移动到缓冲区的开头并准备接收更多数据after[=45] =] 它们,因此在随后的 readflip 之后,您将拥有前一次迭代的未决数据,然后是最新读取操作的数据,准备作为单个序列进行处理。 (这就是 Reader 在解码时内部处理不完整字符序列的方式)