SeekableByteChannel俄罗斯字符

时间:2015-09-03 16:28:47

标签: java encoding java-7 nio cyrillic

最近我开始学习 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 编码制作了一个包含英语和俄语单词的文本文件。这就是我得到的:

output result

方法 buffer.get()返回 byte 值,俄语字符从1000开始。所以我将编码改为 UTF-8 < / strong>并使用了另一种方法:

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

但这给了我一系列问号。

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

1 个答案:

答案 0 :(得分:1)

getChar()的方法ByteBuffer读取两个字节,并将它们解释为char的高字节和低字节,换句话说,总是使用UTF-16编码。通常,您不应尝试手动将字节拼接到String,而不是使用旧的I / O API而不是NIO。只是提一下在尝试手动解码缓冲区中的字节时必须处理的一件事是缓冲区中的字节可能不会以字节边界结束以进行多字节编码。

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

但当然,您可以完全跳过Channel内容并使用Files.newBufferedReader(…)Reader创建Path权限。

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

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,这会将未处理的数据移动到缓冲区的开头,并准备好在之后接收更多数据,所以在后续readflip之后,您具有上一次迭代的待处理数据,然后是最近的读取操作的数据,可以作为单个序列进行处理。 (这是Reader在解码时内部处理不完整字符序列的方式)