Как считать байты из файла java

Обновлено: 07.07.2024

Класс InputStream – это абстрактный класс. Все байтовые потоки чтения наследуются от класса InputStream.

Методы класса InputStream:

Существует 3 основных read() метода:

  • int read()- возвращает целочисленное представление следующего доступного байта в потоке. При достижении конца файла возвращается значение -1.
  • int read(byte[] buffer) - пытается прочесть максимум buffer.length байт из входного потока в массив buffer . Возвращает количество байт, в действительности прочитанных из потока. По достижении конца файла возвращает значение -1.
  • int read(byte[] buffer, int offset, int length) - пытается прочесть максимум length байт, расположив их в массиве buffer , начиная с элемента offset . Возвращает количество реально прочитанных байт. По достижении конца файла возвращает -1.

Методы read() будут блокированы, пока доступные данные не будут прочитаны.

  • int available()- возвращает количество байтов ввода, доступные в данный момент для чтения.
  • close()- закрывает источник ввода. Следующие попытки чтения передадут исключение IOException .
  • long skip(long byteCount) - пропускает byteCount байт ввода, возвращая количество проигнорированных байтов.

Все методы выбрасывают исключение IOException , если происходит ошибка ввода вывода.

2. Класс OutputStream

Все байтовые потоки записи наследуются от абстрактного класса OutputStream.

Методы класса OutputStream:

Существуют 3 основных write() метода:

  • void write(int data) - записывает один байт в выходной поток. Аргумент этого метода имеет тип int , что позволяет вызывать write, передавая ему выражение, при этом не нужно выполнять приведение его типа к byte .
  • void write(byte[] buffer) - записывает в выходной поток весь указанный массив байт.
  • void write(byte[] buffer, int offset, int length)- записывает в поток часть массива - length байт, начиная с элемента buffer[offset] .
  • flush() - очищает любые выходные буферы, завершая операцию вывода.
  • close() - закрывает выходной поток. Последующие попытки записи в этот поток будут возбуждать IOException .

Методы выбрасывают исключение IOException , если происходит ошибка ввода вывода.

3. Класс FileInputStream

Поток FileInputStream используется в Java для чтения данных из файла.

Конструкторы класса FileInputStream:

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

Этот конструктор использует объектный файл с целью создания объекта входного потока для чтения файла.

4. Класс FileOutputStream

В Javа FileOutputStream используется для создания файла и последующей записи в него. Поток создаст файл в случае его отсутствия перед его открытием для вывода.

Конструкторы класса FileOutputStream:

Представленный конструктор использует имя файла в качестве строки с целью создания объекта входного потока для записи файла в Java.

Этот конструктор использует объектный файл с целью создания объекта выходного потока для записи файла.

В 18 уровне начались первые задачи побайтного чтения файлов: прочитать файл, далее найти минимальные/максимальные байты или вывести в упорядоченном виде и т.п.

Побайтовая работа с файлами - 1

  • Ввести с консоли имя файла
  • Считать все байты из файла.
  • Не учитывая повторений - отсортировать их по байт-коду в убывающем порядке.
  • Вывести на экран
  • Закрыть поток ввода-вывода

Решаем в лоб:

Решает все замечательно! Тест (если бы был — прошелся бы на ура). Но в жизни мало файлов содержащих только строчку "Мама мыла раму". Давайте скормим нашей программе файл в 46Мб (по нынешним меркам вроде и не особо много). Что такое, программа выполняется 220 секунд. Попытка скормить с вечера 1Gb файл (размер MPEG4 фильма не в самом лучшем качестве) не увенчалась успехом. Программа утром все еще читала - а мне идти на работу уже. В чем проблема? Наверное в использовании ArrayList<Integer> у которого внутри 1 миллиард элементов. Каждый элемент его занимает 16 байт минимум (Заголовок: 8 байт + Поле int: 4 байта + Выравнивание для кратности 8: 4 байта). Итого мы добровольно загоняем в память 16 Gb данных при размере оперативы в 8. Будем делать лучше. Нырнем в коллекции глубже. И ура, нашлось то, что нам нужно.

Встречаем TreeSet

  • не допускает хранение двух одинаковых элементов (а значит мы будем хранить в памяти все 255 элементов, вместо миллиарда!)
  • при манипуляциях со своими элементами автоматом упорядочивает (само сортирует - вот он, верх совершенства!)

Массив — побайтно

Имеем на выходе: 46Мб файл 158 секунд. 1Gb файл - 2 часа 55 минут. Опять улучшение, но небольшое. И мы сделали все простыми инструментами. Не использовали микроскоп для забивания гвоздей. Теперь лирическое отступление. Вспомним устройство компьютера. Память ОЗУ (DRAM) где обычно выполняется программа и хранятся переменные имеет высокую скорость доступа, но небольшой размер. Память на жестком/flash диске (HDD или Flash-накопители) где обычно хранятся файлы, наоборот имеет низкую скорость доступа, но большой размер. Так что когда мы побайтно читаем 1Gb файл (то есть миллиард раз обращаемся к HDD) - мы тратим много времени на работу с низкоскоростным устройством (по песчинке перекладываем песок с кузова КамАЗа в песочницу). Попробуем еще улучшить.

Байтовые потоки. Классы DataInputStream , DataOutputStream , FileInputStream , FileOutputStream . Примеры использования

В данной теме продемонстрировано использование средств языка Java для работы с байтовыми потоками. Приведены примеры распространенных задач для решения которых используются классы DataInputStream , DataOutputStream , FileInputStream , FileOutputStream .

Содержание

Поиск на других ресурсах:

1. Запись и чтение одномерного массива типа int[] в байтовый файловый поток. Пример

Пусть задан одномерный массив типа int[] , который нужно:

  • записать в байтовый файловый поток;
  • прочитать из байтового файлового потока.

Для решения данной задачи хорошо подойдутклассы:

  • DataInputStream – поток ввода, который содержит методы для чтения данных стандартных типов Java, к которым принадлежит тип int ;
  • DataOutputStream – поток вывода, который содержит методы для записи данных стандартных типов, определенных в Java;
  • FileInputStream – поток ввода, который содержит методы, читающие данные из файла;
  • FileOutputStream – поток вывода, который содержит методы, записывающие данные в файл.

Чтобы организовать запись данных в файл нужно:

  • создать файловый поток типа FileOutputStream ;
  • созданный файловый поток поместить в конструктор DataOutputStream ;
  • использовать методы класса DataOutputStream для записи данных в файл.

Чтобы организовать чтение данных из файла нужно:

  • создать файловый поток типа FileInputStream ;
  • новосозданный файловый поток поместить в конструктор DataInputStream ;
  • использовать методы класса DataInputStream для чтения данных из файла.

Пример.

Результат выполнения программы

2. Запись и чтение данных разных типов ( double , int , char ). Пример
  • записать в файл последовательно значения переменных типов double , int , char ;
  • прочитать значение переменных типов double , int , char .

Результат выполнения программы

4. Пример копирования файлов с использованием байтового потока

В примере приводится фрагмент кода, который осуществляет копирование файлов. Используются средства классов FileInputStream и FileOutputStream .

В Java есть четыре основных абстрактных класса, реализующих потоки ввода-вывода: InputStream, OutputStream, Reader, Writer. Первые два работают с байтами, вторые – с символами.

Для работы с файлами от этих абстрактных классов созданы соответственно классы FileInputStream, FileOutputStream, FileReader, FileWriter. Они являются адаптерами для объектов класса File к "интерфейсам" InputStream, OutputStream, Reader, Writer, т. е. к их методам.

Скажем несколько слов об адаптере как паттерне, или шаблоне, проектирования. Класс-адаптер A наследуется от интерфейса B, к которому приспосабливается объект другого класса – C. Класс-адаптер A имеет поле типа класса объекта C.

Например, объект File адаптируется к потоку ввода InputStream, т. е. все, что мы хотим получить из File, в конечном итоге мы будем получать из InputStream. Фактически мы работаем с InputStream, через адаптер FileInputStream, который с одной стороны наследуется от InputStream, а с другой – имеет поле, которому присваивается объект File.

Адаптер выполняет работу по получению данных из файла и адаптации их к тому виду, который можно передать в методы InputStream. Класс-адаптер, в данном примере – FileInputStream, переопределяет методы InputStream, добавляя в них свой код.

В основной ветке сначала создается объект, для которого требуется адаптер. Затем создается переменная класса, к которому выполняется адаптация. Этой переменной присваивается объект класса-адаптера, в конструктор которого передается адаптируемый объект.

Часто переменную определяют самим классом-адаптером:

В конструктор можно передать строку-адрес. Объект File будет создан внутри адаптера. Пример побайтового копирования файла:

Если используются относительные адреса, они должны начинаться от корня проекта.

В конструктор FileOutputStream можно также передать второй аргумент true. В этом случае, если файл существует, данные в него будут добавляться. Перезаписи файла не произойдет.

Метод available() объекта класса FileInputStream возвращает количество непрочитанных байтов. Метод read() читает один байт и расширяет его до типа int. Кроме этого, есть другой метод read(), читающий массив байт в переменную-аргумент и возвращающий количество реально прочитанных байт. Метод write() также позволяет записывать блоками.

При чтении конца файла блок может содержать меньше прочитанных байт, чем размерность массива. Поэтому write() позволяет указывать срез массива.

У объектов FileOutputStream имеется метод flush(), который принудительно записывает находящиеся в буфере байты на диск. При вызове close() это происходит автоматически.

С помощью класса PrintStream также можно создать поток вывода в файл. PrintStream является наследником FilterOutputStream, который в свою очередь наследник OutputStream как и FileOutputStream.

Функция printf() предназначена для форматированного вывода.

Заметим, переменная System.out является объектом типа PrintStream.

В работе с вводом-выводом также используется другой паттерн проектирования – обертка (wrapper), он же декоратор (decorator). Декоратор расширяет функциональность объекта, а не приспосабливает объект к какому-либо стороннему интерфейсу.

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

В основной ветке создается объект оборачиваемого класса, который передается в конструктор обертки. Внутри класса-обертки есть поле типа декорируемого класса. Этому полю присваивается переданный объект.

BufferedInputStream – класс-обертка для InputStream (наследует через FilterInputStream). В отличие от InputStream класс BufferedInputStream позволяет предварительно читать в буфер порции байт, что уменьшает количество обращений к файлу. Существует также BufferedOutputStream.

Конструктор класса BufferedInputStream принимает объект InputStream или его наследника.

Хотя данные считываются блоками, метод read() извлекает их по одному. Однако в данном случае он будет извлекать их из буфера.

С помощью классов FileReader и FileWriter выполняется ввод-вывод в текстовые файлы.

Метод ready() возвращает истину, если остались непрочитанные символы.

Читать и писать можно блоками. Также методу write() можно передать строку:

Рассматривая ввод данных с клавиатуры, мы уже использовали класс BufferedReader, который наследуется от Reader и позволяет читать отдельные строки методом readLine(). Его также можно использовать для построчного чтения файлов:

Читайте также: