2015-09-03 2 views
-1

Недавно я начал изучать java.nio. И у меня есть пример в моем учебнике, как читать текстовый файл с SeekableByteChannel:SeekableByteChannel russian chars

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() возвращаетбайт значение и русские символы начинаются с где-то 1000. Так что я изменил кодировку для UTF-8 и другой способ:

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

Но это дает мне ряд вопросительных знаков.

Так кто-нибудь знает, как правильно прочитать русский текст, используя SeekableByteChannel?

+0

text.txt можно лучше приклеить к примеру. –

ответ

1

Метод getChar() из ByteBuffer считывает два байта и интерпретирует их как старшие байты и младшие байты в char, других словах, неизменно использует UTF-16 кодирования. Как правило, вы не должны пытаться головоломки байтов до String s вручную, не со старым 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()); 
} 

ByteBuffer знает, сколько байтов он содержит (т.е. были введены в него с помощью операции чтения). С помощью flip вы готовите буфер для их считывания, например. с петлей, как в примере, или путем записи в другой канал. Когда вы знаете, что обработали все содержимое, вы можете использовать clear, чтобы установить буфер в начальном состоянии, где его можно заполнить от начала до конца.

В противном случае, если он может содержать необработанные данные, использовать compact вместо этого, это переместит необработанные данные в начало буфера и подготовить его для получения большего количества данных после их, так что после последующего read и flip у вас есть ожидающие данные предыдущей итерации, за которыми следуют данные последней операции чтения, готовые для обработки в виде одной последовательности.(Так как Reader будет обрабатывать неполные последовательности символов во время декодирования)