Java как использовать файл в качестве input
Отличительной чертой многих языков программирования является работа с файлами и потоками. В Java основной функционал работы с потоками сосредоточен в классах из пакета java.io .
Ключевым понятием здесь является понятие потока . Хотя понятие "поток" в программировании довольно перегружено и может обозначать множество различных концепций. В данном случае применительно к работе с файлами и вводом-выводом мы будем говорить о потоке (stream), как об абстракции, которая используется для чтения или записи информации (файлов, сокетов, текста консоли и т.д.).
Объект, из которого можно считать данные, называется потоком ввода , а объект, в который можно записывать данные, - потоком вывода . Например, если надо считать содержание файла, то применяется поток ввода, а если надо записать в файл - то поток вывода.
В основе всех классов, управляющих потоками байтов, находятся два абстрактных класса: InputStream (представляющий потоки ввода) и OutputStream (представляющий потоки вывода)
Но поскольку работать с байтами не очень удобно, то для работы с потоками символов были добавлены абстрактные классы Reader (для чтения потоков символов) и Writer (для записи потоков символов).
Все остальные классы, работающие с потоками, являются наследниками этих абстрактных классов. Основные классы потоков:
Потоки байтов
Класс InputStream
Класс InputStream является базовым для всех классов, управляющих байтовыми потоками ввода. Рассмотрим его основные методы:
int available() : возвращает количество байтов, доступных для чтения в потоке
void close() : закрывает поток
int read() : возвращает целочисленное представление следующего байта в потоке. Когда в потоке не останется доступных для чтения байтов, данный метод возвратит число -1
int read(byte[] buffer) : считывает байты из потока в массив buffer. После чтения возвращает число считанных байтов. Если ни одного байта не было считано, то возвращается число -1
int read(byte[] buffer, int offset, int length) : считывает некоторое количество байтов, равное length, из потока в массив buffer. При этом считанные байты помещаются в массиве, начиная со смещения offset, то есть с элемента buffer[offset] . Метод возвращает число успешно прочитанных байтов.
long skip(long number) : пропускает в потоке при чтении некоторое количество байт, которое равно number
Класс OutputStream
Класс OutputStream является базовым классом для всех классов, которые работают с бинарными потоками записи. Свою функциональность он реализует через следующие методы:
void close() : закрывает поток
void flush() : очищает буфер вывода, записывая все его содержимое
void write(int b) : записывает в выходной поток один байт, который представлен целочисленным параметром b
void write(byte[] buffer) : записывает в выходной поток массив байтов buffer.
void write(byte[] buffer, int offset, int length) : записывает в выходной поток некоторое число байтов, равное length , из массива buffer , начиная со смещения offset , то есть с элемента buffer[offset] .
Абстрактные классы Reader и Writer
Абстрактный класс Reader предоставляет функционал для чтения текстовой информации. Рассмотрим его основные методы:
absract void close() : закрывает поток ввода
int read() : возвращает целочисленное представление следующего символа в потоке. Если таких символов нет, и достигнут конец файла, то возвращается число -1
int read(char[] buffer) : считывает в массив buffer из потока символы, количество которых равно длине массива buffer. Возвращает количество успешно считанных символов. При достижении конца файла возвращает -1
int read(CharBuffer buffer) : считывает в объект CharBuffer из потока символы. Возвращает количество успешно считанных символов. При достижении конца файла возвращает -1
absract int read(char[] buffer, int offset, int count) : считывает в массив buffer, начиная со смещения offset, из потока символы, количество которых равно count
long skip(long count) : пропускает количество символов, равное count. Возвращает число успешно пропущенных символов
Класс Writer определяет функционал для всех символьных потоков вывода. Его основные методы:
Writer append(char c) : добавляет в конец выходного потока символ c. Возвращает объект Writer
Writer append(CharSequence chars) : добавляет в конец выходного потока набор символов chars. Возвращает объект Writer
abstract void close() : закрывает поток
abstract void flush() : очищает буферы потока
void write(int c) : записывает в поток один символ, который имеет целочисленное представление
void write(char[] buffer) : записывает в поток массив символов
absract void write(char[] buffer, int off, int len) : записывает в поток только несколько символов из массива buffer. Причем количество символов равно len, а отбор символов из массива начинается с индекса off
void write(String str) : записывает в поток строку
void write(String str, int off, int len) : записывает в поток из строки некоторое количество символов, которое равно len, причем отбор символов из строки начинается с индекса off
Функционал, описанный классами Reader и Writer, наследуется непосредственно классами символьных потоков, в частности классами FileReader и FileWriter соответственно, предназначенными для работы с текстовыми файлами.
Все эти вещи мы можем назвать одним словом — процесс обмена данными между программой и внешним миром. Хотя это уже не одно слово.
Сам процесс обмена данными можно разделить на два типа: получение данных и отправка данных. Например, вы считываете данные с клавиатуры с помощью объекта Scanner — это получение данных. И выводите данные на экран с помощью команды System.out.println() — это отправка данных.
Для описания процесса обмена данными в программировании используется термин поток. Откуда вообще взялось такое название?
В реальной жизни им может быть поток воды или поток людей (людской поток). В программировании же под потоком подразумевают поток данных.
Потоки — это универсальный инструмент. Они позволяют программе получать данные откуда угодно (входящие потоки) и отправляют данные куда угодно (исходящие потоки). Делятся на два вида:
Чтобы потоки можно было «потрогать руками», разработчики Java написали два класса: InputStream и OutputStream .
У класса InputStream есть метод read() , который позволяет читать из него данные. А у класса OutputStream есть метод write() , который позволяет записывать в него данные. У них есть и другие методы, но об этом после.
Байтовые потоки
Что же это за данные и в каком виде их можно читать? Другими словами, какие типы данных поддерживаются этими классами?
О, это универсальные классы, и поэтому они поддерживают самый распространённый тип данных — byte . В OutputStream можно записывать байты (и массивы байт), а из объекта InputStream можно читать байты (или массивы байт). Все — никакие другие типы данных они не поддерживают.
Поэтому такие потоки еще называют байтовыми потоками .
Особенность потоков в том, что данные из них можно читать (писать) только последовательно. Вы не можете прочитать данные из середины потока, не прочитав все данные перед ними.
Именно так работает чтение с клавиатуры через класс Scanner : вы читаете данные с клавиатуры последовательно: строка за строкой. Прочитали строку, прочитали следующую строку, прочитали следующую строку и т.д. Поэтому метод чтения строки и называется nextLine() (дословно — «следующая срока»).
Запись данных в поток OutputStream тоже происходит последовательно. Хороший пример — вывод на экран. Вы выводите строку, за ней еще одну и еще одну. Это последовательный вывод. Вы не можете вывести 1-ю строку, затем 10-ю, а затем вторую. Все данные записываются в поток вывода только последовательно.
Символьные потоки
Недавно вы изучали, что строки — второй по популярности тип данных, и это действительно так. Очень много информации передается в виде символов и целых строк. Компьютер отлично бы передавал все в виде байт, но люди не настолько идеальны.
Java-программисты учли этот факт и написали еще два класса: Reader и Writer . Класс Reader — это аналог класса InputStream , только его метод read() читает не байты, а символы — char . Класс Writer соответствует классу OutputStream , и так же, как и класс Reader , работает с символами ( char ), а не байтами.
Если сравнить эти четыре класса, мы получим такую картину:
Байты (byte) | Символы (char) |
---|---|
Чтение данных | |
Запись данных |
Практическое применение
Сами классы InputStream , OutputStream , Reader и Writer в явном виде никто не использует: они не присоединены ни к каким внешним объектам, из которых можно читать данные (или в которые можно писать данные). Однако у этих четырех классов много классов-наследников, которые умеют очень многое.
2. Класс InputStream
Класс InputStream интересен тем, что является классом-родителем для сотен классов-наследников. В нем самом нет никаких данных, однако у него есть методы, которые есть у всех его классов-наследников.
Объекты-потоки вообще редко хранят в себе данные. Поток — это инструмент чтения/записи данных, но не хранения. Хотя бывают и исключения.
Методы класса InputStream и всех его классов-наследников:
Методы | Описание |
---|---|
Читает один байт из потока | |
Читает массив байт из потока | |
Читает все байты из потока | |
Пропускает n байт в потоке (читает и выкидывает) | |
Проверяет, сколько байт еще осталось в потоке | |
Закрывает поток |
Вкратце пройдемся по этим методам:
Метод read()
Метод read() читает один байт из потока и возвращает его. Вас может сбить тип результата — int , однако так было сделано, потому что тип int — это стандарт всех целых чисел. Три первые байта типа int будут равны нулю.
Метод read(byte[] buffer)
Это вторая модификация метода read() . Он позволяет считать из InputStream сразу массив байт. Массив для сохранения байт нужно передать в качестве параметра. Метод возвращает число — количество реально прочитанных байт.
Допустим у вас буфер на 10 килобайт, и вы читаете данные из файла с помощью класса FileInputStream . Если файл содержит всего 2 килобайта, все данные будут помещены в массив-буфер, а метод вернет число 2048 (2 килобайта).
Метод readAllBytes()
Очень хороший метод. Просто считывает все данные из InputStream , пока они не закончатся, и возвращает их виде единого массива байт. Очень удобен для чтения небольших файлов. Большие файлы могут физически не поместиться в память, и метод кинет исключение.
Метод skip(long n)
Этот метод позволяет пропустить n первых байт из объекта InputStream . Поскольку данные читаются строго последовательно, этот метод просто вычитывает n первых байт из потока и выбрасывает их.
Возвращает число байт, которые были реально пропущены (если поток закончился раньше, чем прокрутили n байт).
Метод int available()
Метод возвращает количество байт, которое еще осталось в потоке
Метод void close()
Метод close() закрывает поток данных и освобождает связанные с ним внешние ресурсы. После закрытия потока данные из него читать больше нельзя.
Давайте напишем пример программы, которая копирует очень большой файл. Его нельзя весь считать в память с помощью метода readAllBytes() . Пример:
InputStream для чтения из файла
OutputStream для записи в файл
Буфер, в который будем считывать данные
Пока данные есть в потоке
В этом примере мы использовали два класса: FileInputStream — наследник InputStream для чтения данных из файла, и класс FileOutputStream — наследник OutputStream для записи данных в файл. О втором классе расскажем немного позднее.
Еще один интересный момент — это переменная real . Когда из файла будет читаться последний блок данных, легко может оказаться, что его длина меньше 64Кб. Поэтому в output нужно тоже записать не весь буфер, а только его часть: первые real байт. Именно это и делается в методе write() .
Существуют две параллельные иерархии классов ввода : InputStream и Reader. Класс Reader введен в последних версиях Java. В данной статье рассматривается вопрос использования потока байтового ввода InputStream, иерархия которого представлена на следующем рисунке.
Поток Stream- это абстрактное понятие источника или приёмника данных, которые способны обрабатывать информацию. Есть два типа потоков: байтовые и символьные. В некоторых ситуациях символьные потоки более эффективны, чем байтовые. Классы, производные от базовых InputStream или Reader, имеют методы read() для чтения отдельных байтов или массива байтов.
Базовый класс InputStream - это абстрактный класс, определяющий входной поток данных, и является родителем для классов, получающих данные из различных источников : массив байтов, строки (String), файлы, каналы pipe, у которых одна из сторон является входом, а вторая сторона играет роль выхода, и т.д. Методы класса InputStream при возникновении ошибки вызывают исключение IOException.
Методы класса InputStream :
Метод | Описание |
---|---|
int read() | возвращает очередной доступный символ во входном потоке в виде целого |
int read(byte b[]) | чтение b.length байтов из входного потока в массив b. Возвращает количество прочитанных из потока байтов |
int read(byte b[], int off, int len) | чтение len байтов в массиве b, начиная со смещения off. Возвращает количество реально прочитанных байтов |
long skip(long n) | пропуск во входном потоке n байтов. Возвращает количество пропущенных байтов |
int available() | получение количество доступных для чтения байтов |
void close() | закрытие источник ввода. Последующие попытки чтения из этого потока вызывают IOException |
void mark(int readlimit) | установка метки в текущей позиции входного потока, которую можно будет использовать до тех пор, пока из потока не будет прочитано readlimit байтов |
void reset() | перевод указателя потока на установленную ранее метку |
boolean markSupported() | проверка поддержки потоком операции mark/reset |
Класс InputStream часто выступает в качестве параметров конструкторов или методов различных классов. Согласно правилам наследования это означает, что в качестве параметра может быть передан объект любого класса-наследника. Это позволяет комбинировать классы для достижения нужных нам целей.
ByteArrayInputStream
Класс ByteArrayInputStream использует байтовый массив в качестве источника данных. Он имеет следующие конструкторы :
В качестве параметров конструкторы ByteArrayInputStream используют массив байтов buf для чтения, смещение относительно начала массива offset и количество считываемых символов length.
Пример чтения массив байтов в консоль:
В отличие от других классов потоков для закрытия объекта ByteArrayInputStream не требует вызывать метод close.
FileInputStream
FileInputStream - основной класс из данной иерархии для работы с файлами. Имеет два основных конструктора.
Подробно FileInputStream описан на странице Файлы и директории
FilterInputStream
FilterInputStream - абстрактный класс, предоставляющий интерфейс для классов-надстроек, которые добавляют к существующим потокам полезные свойства. FilterInputStream является базовым классом для двух других классов. Его единственный конструктор требует передачи в качестве параметра объекта класса InputStream, т.е. фактически объекта некоторого неабстрактного класса, порожденного от InputStream.
Прямое использование FilterInputStream в программе нецелесообразно.
BufferedInputStream
BufferedInputStream служит для организации более эффективного "буферизованного" ввода данных. Буферизация ввода-вывода является удобным способом оптимизации производительности, позволяя заключить в оболочку любой поток класса InputStream.
В конструкторе класса BufferedInputStream необходимо передать InputStream. В данном случае таким объектом является экземпляр класса ByteArrayInputStream.
Как и все потоки ввода BufferedInputStream обладает методом read(), который считывает данные с помощью метода read из массива buffer.
Фактические все то же самое можно было сделать и с помощью одного ByteArrayInputStream, не прибегая к буферизированному потоку. Класс BufferedInputStream просто оптимизирует производительность при работе с потоком ByteArrayInputStream.
DataInputStream
Для чтения байтовых данных (не строк) применяется класс DataInputStream. В этом случае необходимо использовать классы из группы InputStream. Для преобразования строки в массив байтов, пригодный для помещения в поток ByteArrayInputStream, в классе String предусмотрен метод getBytes(). Полученный ByteArrayInputStream представляет собой поток InputStream, подходящий для передачи DataInputStream.
При побайтовом чтении символов из форматированного потока DataInputStream методом readByte() любое полученное значение будет считаться действительным, поэтому возвращаемое значение неприменимо для идентификации конца потока. Вместо этого можно использовать метод available(), который сообщает, сколько еще осталось символов.
Класс DataInputStream позволяет читать элементарные данные из потока через интерфейс DataInput, который определяет методы, преобразующие элементарные значения в форму последовательности байтов. Такие потоки облегчают сохранение в файле двоичных данных.
Конструктор класса DataInputStream:
Методы DataInputStream
Метод | Описание |
---|---|
boolean readBoolean() | байт булевого однобайтового значения |
byte readByte() | байт одного байта |
char readChar() | байт значения char |
double readDouble() | байт восьмибайтового значения double |
float readFloat() | чтение четырехбайтового значения float |
int readInt() | чтение целочисленного значения int |
long readLong() | чтение значения long |
short readShort() | чтение значения short |
String readUTF() | чтение строки в кодировке UTF-8 |
int skipBytes(int n) | пропуск при чтении n байтов |
Пример чтения из бинарного файла с использованием DataInputStream
ObjectInputStream
Класс ObjectInputStream отвечает за чтение ранее сериализованных данных из потока. В конструкторе он принимает ссылку на поток ввода:
Основные методы класса ObjectInputStream :
Метод | Описание |
---|---|
int read() | чтение одного байта; возвращает его целочисленное представление |
boolean readBoolean() | чтение одного значения boolean |
byte readByte() | чтение одного байта |
char readChar() | чтение одного символ char |
double readDouble() | чтение значения типа double |
float readFloat() | чтение значения типа float |
int readInt() | чтение целочисленного значения int |
long readLong() | чтение значения типа long |
short readShort() | чтение значения типа short |
String readUTF() | чтение строки в кодировке UTF-8 |
Object readObject() | чтение объекта |
int skipBytes(int len) | пропуск при чтении нескольких байт, количество которых равно len |
int available() | чтение количества доступных для чтения байт |
void close() | закрытие потока |
Пример чтения объекта Person из файла :
Класс PipedInputStream
Класс PipedInputStream - это специальный класс, используемый для связи отдельных программ (потоков) друг с другом внутри одной JVM. Данный класс является важным инструментом организации синхронизации потоков.
Пакет java.io содержит почти все классы, которые вам могут понадобиться для ввода и вывода (ввода / вывода) в Java. Все эти потоки представляют собой источник ввода и назначение вывода. Поток в пакете java.io поддерживает много данных, таких как примитивы, объекты, локализованные символы и т. Д.
Поток
Байтовые потоки
В качестве следующего шага скомпилируйте вышеуказанную программу и выполните ее, что приведет к созданию файла output.txt с тем же содержимым, что и в input.txt. Итак, давайте поместим приведенный выше код в файл CopyFile.java и сделаем следующее:
Потоки персонажей
В качестве следующего шага скомпилируйте вышеуказанную программу и выполните ее, что приведет к созданию файла output.txt с тем же содержимым, что и в input.txt. Итак, давайте поместим приведенный выше код в файл CopyFile.java и сделаем следующее:
Стандартные потоки
Все языки программирования обеспечивают поддержку стандартного ввода-вывода, когда программа пользователя может принимать ввод с клавиатуры, а затем выводить ее на экран компьютера. Если вы знакомы с языками программирования C или C ++, вы должны знать о трех стандартных устройствах STDIN, STDOUT и STDERR. Точно так же Java предоставляет следующие три стандартных потока:
Чтение и запись файлов
Как описано ранее, поток может быть определен как последовательность данных. InputStream используется для чтения данных из источника, а OutputStream используется для записи данных в место назначения.
Вот иерархия классов для работы с потоками ввода и вывода.
Двумя важными потоками являются FileInputStream и FileOutputStream , которые будут обсуждаться в этом руководстве.
FileInputStream
Этот поток используется для чтения данных из файлов. Объекты могут быть созданы с помощью ключевого слова new, и существует несколько типов конструкторов.
Следующий конструктор принимает объект файла для создания объекта входного потока для чтения файла. Сначала мы создаем объект файла, используя метод File () следующим образом:
Когда у вас есть объект InputStream , появляется список вспомогательных методов, которые можно использовать для чтения в потоке или для выполнения других операций в потоке.
public void close () генерирует IOException <>
Этот метод закрывает поток вывода файла. Освобождает любые системные ресурсы, связанные с файлом. Выдает IOException.
Защищенный void finalize () выбрасывает IOException <>
Этот метод очищает соединение с файлом. Гарантирует, что метод close этого потока вывода файла вызывается, когда больше нет ссылок на этот поток. Выдает IOException.
public int read (int r) выдает IOException <>
Этот метод читает указанный байт данных из InputStream. Возвращает int. Возвращает следующий байт данных, и -1 будет возвращено, если это конец файла.
Читайте также: