Подтвердить что ты не робот

Почему я могу читать только 1024 байта за один раз с помощью ObjectInputStream?

Я написал следующий код, который записывает 4000 байтов 0s в файл test.txt. Затем я читаю один и тот же файл в кусках по 1000 байт за раз.

FileOutputStream output = new FileOutputStream("test.txt");
ObjectOutputStream stream = new ObjectOutputStream(output);

byte[] bytes = new byte[4000];

stream.write(bytes);
stream.close();

FileInputStream input = new FileInputStream("test.txt");
ObjectInputStream s = new ObjectInputStream(input);


byte[] buffer = new byte[1000];
int read = s.read(buffer);

while (read > 0) {
    System.out.println("Read " + read);
    read = s.read(buffer);
}

s.close();

То, что я ожидаю, - это прочитать 1000 байтов четыре раза.

Read 1000
Read 1000
Read 1000
Read 1000

Однако, что на самом деле происходит, так это то, что я кажусь "приостановленным" (за отсутствием лучшего слова) каждые 1024 байта.

Read 1000
Read 24
Read 1000
Read 24
Read 1000
Read 24
Read 928

Если я попытаюсь прочитать более 1024 байта, тогда я получаю ограничение на 1024 байта. Если я пытаюсь читать менее 1024 байт, мне все равно необходимо приостановить отметку 1024 байта.

После проверки выходного файла test.txt в шестнадцатеричном формате, я заметил, что существует последовательность из 5 ненулевых байтов 7A 00 00 04 00 1029 байт, несмотря на то, что я написал только 0s в файл. Вот результат из моего шестнадцатеричного редактора. (Будет слишком длинным, чтобы соответствовать вопросу.)

Итак, мой вопрос: почему эти пять байтов появляются в моем файле, когда я написал полностью 0s? Эти 5 байтов имеют какое-то отношение к паузе, которая возникает каждые 1024 байта? Почему это необходимо?

4b9b3361

Ответ 1

Объектные потоки используют внутренний 1024-байтовый буфер и записывают примитивные данные в кусках этого размера в блоках потока, возглавляемых маркерами Data Data, которые предполагают, что 0x7A, за которым следует 32-битный (или 0x77, за которым следует слово длиной 8 бит). Таким образом, вы можете читать только максимум 1024 байта.

Реальный вопрос: почему вы используете потоки объектов только для чтения и записи байтов. Используйте буферизованные потоки. Тогда буферизация находится под вашим контролем, и, кстати, там нет нулевого пространства, в отличие от потоков объектов, которые имеют потоковые заголовки и коды типов.

Сериализованные данные NB не являются текстовыми и не должны храниться в файлах с именем .txt.

Ответ 2

ObjectOutputStream и ObjectInputStream - специальные потоки, используемые для сериализации объектов.

Но когда вы делаете stream.write(bytes);, вы пытаетесь использовать ObjectOutputStream как обычный поток, для записи 4000 байтов, а не для записи объекта array-of-bytes. Когда данные записываются так: ObjectOutputStream, они обрабатываются специально.

Из документации ObjectOutputStream:

(внимание мое.)

Примитивные данные, исключая сериализуемые поля и внешние данные, записываются в ObjectOutputStream в записи блоков данных. Запись данных блока состоит из заголовка и данных. Заголовок блока данных состоит из маркера и количества байтов, следующих за заголовком. Последовательные записи примитивных данных объединяются в одну запись блоков данных. Коэффициент блокировки, используемый для записи блочных данных, будет 1024 байта. Каждая запись блоков данных заполняется до 1024 байтов или записывается всякий раз, когда происходит прекращение режима блочных данных.

Надеюсь, из этого ясно, почему вы получаете такое поведение.

Я бы рекомендовал использовать BufferedOutputStream вместо ObjectOutputStream, или, если вы действительно хотите использовать ObjectOutputStream, используйте writeObject() вместо write(). Соответствующее относится к вводу.

Ответ 3

Я предлагаю вам использовать try-with-resources Statement, чтобы обрабатывать закрытие ваших ресурсов, добавлять буферизацию с помощью BufferedInputStream и BufferedOutputStream, а затем используйте writeObject и readObject для сериализации byte[]. Что-то вроде,

try (OutputStream output = new BufferedOutputStream(//
        new FileOutputStream("test.txt"), 8192); //
        ObjectOutputStream stream = new ObjectOutputStream(output)) {
    byte[] bytes = new byte[4000];

    stream.writeObject(bytes);
} catch (IOException ioe) {
    ioe.printStackTrace();
}

а затем читать как

try (InputStream input = new BufferedInputStream(//
        new FileInputStream("test.txt"), 8192); //
        ObjectInputStream s = new ObjectInputStream(input)) {
    byte[] bytes = (byte[]) s.readObject();
} catch (IOException | ClassNotFoundException ioe) {
    ioe.printStackTrace();
}

Если есть частичные массивы, вам нужно добавить длину. Вы можете использовать stream.writeInt(len); и int len = stream.readInt(); с другой стороны.