Неожиданный результат с RandomAccessFile

Я пытаюсь узнать о RandomAccessFile, но после создания тестовой программы я получаю странный вывод.

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileTest
{
    public static void main(String[] args) throws IOException
    {
        // Create a new blank file
        File file = new File("RandomAccessFileTest.txt");
        file.createNewFile();
        
        // Open the file in read/write mode
        RandomAccessFile randomfile = new RandomAccessFile(file, "rw");
        
        // Write stuff
        randomfile.write("Hello World".getBytes());
        
        // Go to a location
        randomfile.seek(0);
        
        // Get the pointer to that location
        long pointer = randomfile.getFilePointer();
        System.out.println("location: " + pointer);
        
        // Read a char (two bytes?)
        char letter = randomfile.readChar();
        System.out.println("character: " + letter);
        
        randomfile.close();
    }
}

Эта программа распечатывает

местоположение: 0

персонаж: ?

Оказывается, значение буквы было «䡥», хотя должно быть «H».

Я нашел вопрос, похожий на этот, и, по-видимому, это вызвано чтением одного байта вместо двух, но не объяснил, как именно это исправить.


person RandomPerson78642    schedule 15.01.2015    source источник
comment
Почему бы не использовать writeChars? Всегда читайте и пишите с одной и той же кодировкой.   -  person Radiodef    schedule 15.01.2015


Ответы (1)


Вы написали «Hello World» в кодировке платформы по умолчанию, которая, вероятно, использует один байт на символ.

Вы читаете RandomAccessFile.readChar который всегда читает два байта. Документация:

Читает символ из этого файла. Этот метод считывает два байта из файла, начиная с текущего указателя файла. Если прочитаны байты по порядку b1 и b2, где 0 ‹= b1, b2 ‹= 255, то результат будет равен:

   (char)((b1 << 8) | b2)

Этот метод блокируется до тех пор, пока не будут прочитаны два байта, не будет обнаружен конец потока или не возникнет исключение.

Таким образом, H и e объединяются в один символ — H — это U+0048, e — это U+0065, поэтому, предполагая, что они были записаны как символ ASCII, вы читаете байты 0x48 и 0x65 и объединяете их в U+4865, который является символом хань для "движущейся тележки".

По сути, вы не должны использовать readChar для чтения этих данных.

Обычно, чтобы прочитать текстовый файл, вы хотите, чтобы InputStreamReader (с соответствующей кодировкой) обертывало InputStream (например, FileInputStream). Попытка сделать это с помощью RandomAccessFile не совсем идеальна — вы можете считать данные в byte[], а затем преобразовать их в String, но есть множество тонкостей, о которых вам нужно подумать.

person Jon Skeet    schedule 15.01.2015
comment
Аааа, я вижу. Есть ли альтернативный метод записи? Я пытался использовать RandomAccessFile.writeChars, но это дало мне нежелательный символ NULL после каждого символа. - person RandomPerson78642; 15.01.2015
comment
@RandomPerson78642: Ну, на самом деле мы недостаточно знаем о вашем контексте или о том, почему вы используете RandomAccessFile для начала. Я бы лично старался избегать этого для текстовых данных в большинстве случаев. - person Jon Skeet; 15.01.2015
comment
writeChar не записывает нежелательные нулевые символы, он записывает ваши символы как два байта. - person eckes; 15.01.2015
comment
Хорошо, ничего страшного. Вы были правы, оказывается, мои проблемы были вызваны смешением байтов и символов. Я переключил все на байты, и теперь все работает отлично. - person RandomPerson78642; 16.01.2015