Как считать байты из файла 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 уровне начались первые задачи побайтного чтения файлов: прочитать файл, далее найти минимальные/максимальные байты или вывести в упорядоченном виде и т.п.
- Ввести с консоли имя файла
- Считать все байты из файла.
- Не учитывая повторений - отсортировать их по байт-коду в убывающем порядке.
- Вывести на экран
- Закрыть поток ввода-вывода
Решаем в лоб:
Решает все замечательно! Тест (если бы был — прошелся бы на ура). Но в жизни мало файлов содержащих только строчку "Мама мыла раму". Давайте скормим нашей программе файл в 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(). Его также можно использовать для построчного чтения файлов:
Читайте также: