Разное

Файловый ввод вывод java – Java — Потоки ввода/вывода, файлы и каталоги / ProgLang

Содержание

Java | Потоки ввода-вывода

Потоки ввода-вывода

Последнее обновление: 25.04.2018

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

Ключевым понятием здесь является понятие потока. Хотя понятие «поток» в программировании довольно перегружено и может обозначать множество различных концепций. В данном случае применительно к работе с файлами и вводом-выводом мы будем говорить о потоке (stream), как об абстракции, которая используется для чтения или записи информации (файлов, сокетов, текста консоли и т.д.).

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

Объект, из которого можно считать данные, называется потоком ввода, а объект, в который можно записывать данные, — потоком вывода. Например, если надо считать содержание файла, то применяется поток ввода, а если надо записать в файл — то поток вывода.

В основе всех классов, управляющих потоками байтов, находятся два абстрактных класса: 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 соответственно, предназначенными для работы с текстовыми файлами.

Теперь рассмотрим конкретные классы потоков.

metanit.com

Система ввода и вывода. Работа с файлами.

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

Вообще, Java очень богата на методы стандартных классов и зачастую, их может хватить на разработку серьезных приложений. В частности, работа с файлами и ввод/вывод можно организовать стандартными средствами, уложившись в пару строчек кода.

Еще одно предисловие перед теорией: эта статья может показаться несколько сложнее, чем те, которые мы учили раньше. Для организации потоков ввода и вывода нужно будет «сооружать» не малые конструкции. Сказать по правде, я и сам иногда забываю, что за чем следует. Поэтому, не нужно паниковать, если Вы запутались в объеме материала. Все придет со временем, когда Вы будете активно программировать.

Когда говорят о потоках ввода или вывода, обычно подразумевают файл или передачу по сети, иногда консоль. В Java неважно с каким видом ввода/вывода Вы работаете. Для всех средств нужно создать поток (stream) и уже работать с ним. Ввод представлен в классе InputStream, вывод — в OutputStream. Они обеспечивают общий интерфейс для всех подклассов. Все подклассы, которые от них наследуются реализуют ввод/вывод для разных форм данных. Stream инкапсулирует процессы низкого уровня. Алгоритм работы с потоковой передачей следующий:

  1. Создается поток
  2. Поток открыт
  3. Выполняет любое необходимое действие.
  4. Поток закрыт.

Вот все 4 действия, которые нужно совершить, чтобы считать или записать данные. На практике все станет еще проще. Прежде чем мы начнем программировать нужно, чтобы Вы посмотрели на схему классов, которые наследуются от InputStream и OutputStream. Запоминать их необязательно, нужно просто понимать иерархию.

Название классов говорят, какой из них за что отвечает.

Хочу обратить Ваше внимание на классах BufferedInputStream и BufferedOutputStream. Они позволяют работать с потоком не напрямую, а через буфер. Это значительно улучшает производительность программы.

Еще очень интересный класс RandomAccessFile. Он позволяет одновременно и читать и писать в файл. Для файла используется класс File, в конструктор которого можно прописать путь к файлу:

  1. package com.java_master;

  2.  

  3. import java.io.File;

  4. import java.io.FileNotFoundException;

  5. import java.io.IOException;

  6. import java.io.RandomAccessFile;

  7.  

  8. public class ReadAndWriteFileExample {

  9.    

  10.     public static void readFile() throws IOException{//некоторые методы выбрасывают исключения
  11.         //скоро мы доберемся до исключений

  12.         File file = new File(«test.txt»);//указываем полный путь к файлу.
  13.         //в конструктор передаем файл и строку «r» которая указывает, что файл можно только читать

  14.         StringBuilder sb = new StringBuilder();//нам нужно будет конкатенировать много строк

  15.         //String может значительно потребить ресурсы

  16.         int b = randomAccessFile.read();//читаем поток байтов

  17.         while(b != -1){//-1 будет означать конец файла. Если не писать этой строки, то прочитается только первая строка файла

  18.             sb = sb.append((char)b);//добавляем к нашей строке поток байтов, который мы прикастили к char. Если не кастить

  19.             //то выведет цифры, байтов

  20.             b = randomAccessFile.read();//читаем файл и записываем в поток

  21.         }

  22.         randomAccessFile.close();//не забываем закрывать поток

  23.         System.out.println(sb);//вывод в консоль
  24.  

  25.     }

  26.    

  27.         File file = new File(«test.txt»);//файл находится в нашем проекте
  28.         randomAccessFile.write(data.getBytes());//так как мы не можем работать с символами напрямую, нужно перевести строку в байты

  29.         randomAccessFile.close();//закрываем поток

  30.     }

  31.  

  32.         ReadAndWriteFileExample.writeFile(«Some my data»);

  33.         ReadAndWriteFileExample.readFile();

  34.  

  35.     }

  36.  

  37. }

 

Наш файл test.txt до запуска программы:

test.txt после запуска программы:

Как видите, запись перезаписала первую строку. В классе RandomAccessFile есть метод который позволяет читать и записывать в файл с определенной точки: randomAccessFile.seek(количество символов начиная с начала). Используя этот метод, можно писать файл и не перезаписывать предыдущие записи.

Хотя вышеперечисленные классы позволяют работать с потоком, Вы не можете напрямую прочитать или записать текст.В примере выше нам приходилось кастить байты к char и переводить строку в байты. Для того, чтобы увидеть символы без ухищрений в читаемом для человека формате нужно использовать классы Reader и Writer. Эти классы похожие на BufferedInputStream и BufferedOutputStream только позволяют работать с символами в формате юникод. Вот их иерархия:

java-master.com

Ввод-вывод в Java. Классы FileInputStream, FileOutputStream, BufferedInputStream

Привет! В сегодняшней лекции продолжим разговор о потоках ввода и вывода в Java, или сокращенно — Java I/O («input-output»). Это не первая лекция на данную тему, и далеко не последняя 🙂 Так уж получилось, что Java как язык предоставляет много возможностей по работе с вводом-выводом. Классов, которые реализуют эту функциональность довольно много, поэтому мы разделили их на несколько лекций, чтобы ты поначалу не запутался 🙂 В прошлых лекциях мы коснулись BufferedReader’a, а также абстрактных классов InputStream & OutputStream и нескольких наследников. Сегодня рассмотрим 3 новых класса: FileInputStream, FileOutputStream и BufferedInputStream.

Класс FileOutputStream

Главное назначение класса FileOutputStream — запись байтов в файл. Ничего сложного 🙂 FileOutputStream является одной из реализаций абстрактного класса
OutputStream
. В конструкторе объекты этого класса принимают либо путь к целевому файлу (в который и нужно записать байты), либо объект класса File. Рассмотрим оба примера:
public class Main {

   public static void main(String[] args) throws IOException {


       File file = new File("C:\\Users\\Username\\Desktop\\test.txt");
       FileOutputStream fileOutputStream = new FileOutputStream(file);

       String greetings = "Привет! Добро пожаловать на JavaRush - лучшй сайт для тех, кто хочет стать программистом!";

       fileOutputStream.write(greetings.getBytes());

       fileOutputStream.close();
   }
}
При создании объекта File мы указали в конструкторе путь, где он должен будет находиться. Создавать его заранее нет необходимости: если он не существует, программа создаст его сама. Можно обойтись и без создания лишнего объекта, и просто передать строку с адресом:
public class Main {

   public static void main(String[] args) throws IOException {


       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt");

       String greetings = "Привет! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!";

       fileOutputStream.write(greetings.getBytes());

       fileOutputStream.close();
   }
}
Результат в обоих случаях будет одинаковым. Мы можем открыть наш файл и увидеть там: Привет! Добро пожаловать на JavaRush — лучший сайт для тех, кто хочет стать программистом! Однако есть здесь один нюанс. Попробуй запустить код из примера выше несколько раз подряд, а потом загляни в файл, и ответь на вопрос: сколько записанных в него строк ты видишь? Всего одну. Но ведь ты запускал код несколько раз. Однако при этом данные, оказывается, всякий раз перезаписывались, заменяя старые. Что делать, если нас это не устраивает, и требуется последовательная запись? Что если мы хотим записать наше приветствие в файл три раза подряд? Здесь все просто. Поскольку сам язык не может знать, какое именно поведение нам нужно в каждом случае, в конструктор
FileOutputStream
ты можешь передать дополнительный параметр — boolean append. Если его значение true, данные будут дозаписаны в конец файла. Если false (а по умолчанию это значение и есть false), старые данные будут стерты, а новые записаны. Давай проверим и запустим наш измененный код трижды:
public class Main {

   public static void main(String[] args) throws IOException {


       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt", true);

       String greetings = "Привет! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!\r\n";

       fileOutputStream.write(greetings.getBytes());

       fileOutputStream.close();
   }
}
Результат в файле: Привет! Добро пожаловать на JavaRush — лучший сайт для тех, кто хочет стать программистом! Привет! Добро пожаловать на JavaRush — лучший сайт для тех, кто хочет стать программистом! Привет! Добро пожаловать на JavaRush — лучший сайт для тех, кто хочет стать программистом!
Другое дело! Не забывай об этой особенности при использовании классов ввода-вывода. В свое время и мне приходилось часами сидеть над задачами, чтобы понять, куда деваются из файлов мои старые данные 🙂 Ну и конечно, как и в случае с другими классами I/O, не забываем об освобождении ресурсов через метод close().

Класс FileInputStream

У класса FileInputStream назначение противоположное — чтение байтов из файла. Так же как FileOutputStream наследует OutputStream, этот класс происходит от абстрактного класса InputStream. Запишем в наш текстовый «test.txt» несколько строк текста: «So close no matter how far Couldn’t be much more from the heart Forever trusting who we are And nothing else matters» Вот как будет выглядеть реализация чтения данных из файла при помощи FileInputStream:
public class Main {

   public static void main(String[] args) throws IOException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");

       int i;

       while((i=fileInputStream.read())!= -1){

           System.out.print((char)i);
       }
   }
}
Мы считываем из файла по одному байту, преобразуем считанные байты в файлы и выводим их в консоль. А вот и результат в консоли: So close no matter how far Couldn’t be much more from the heart Forever trusting who we are And nothing else matters

Класс BufferedInputStream

Думаю, учитывая знания из прошлых лекций, ты легко сможешь сказать, зачем нужен класс BufferedInputStream и какие преимущества у него есть по сравнению с FileInputStream 🙂 Мы уже встречались с буферизированными потоками, поэтому попробуй предположить (или вспомнить), прежде чем продолжить чтение 🙂 Буферизированные потоки нужны прежде всего для оптимизации ввода-вывода. Обращение к источнику данных, например, чтение из файла, — дорогостоящая в плане производительности операция. И каждый раз обращаться к файлу для чтения по одному байту расточительно. Поэтому BufferedInputStream считывает данные не по одному байту, а блоками и временно хранит их в специальном буфере. Это позволяет нам оптимизировать работу программы за счет того, что мы уменьшаем количество обращений к файлу. Давай посмотрим, как это выглядит:
public class Main {

   public static void main(String[] args) throws IOException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt");

       BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 200);

       int i;

       while((i = bufferedInputStream.read())!= -1){

           System.out.print((char)i);
       }
   }
}
Здесь мы создали объект BufferedInputStream. Он принимает на вход объект InputStream или любого его наследника, так что предыдущий FileInputStream подойдет. В качестве дополнительного параметра он принимает размер буфера в байтах. Теперь благодаря этому данные будут считываться из файла не по одному байту, а по 200! Представь, насколько мы сократили количество обращений к файлу. Для сравнения производительности ты можешь взять какой-нибудь большой текстовый файл размером несколько мегабайт и сравнить, сколько займет его чтение и вывод в консоль в миллисекундах с использованием FileInputStream и BufferedInputStream. Вот оба варианта кода для примера:
public class Main {

   public static void main(String[] args) throws IOException {

       Date date = new Date();

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\textBook.rtf");

       BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

       int i;

       while((i = bufferedInputStream.read())!= -1){

           System.out.print((char)i);
       }

       Date date1 = new Date();

       System.out.println((date1.getTime() - date.getTime()));
   }
}



public class Main {

   public static void main(String[] args) throws IOException {

       Date date = new Date();

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\26951280.rtf");


       int i;

       while((i = fileInputStream.read())!= -1){

           System.out.print((char)i);
       }

       Date date1 = new Date();

       System.out.println((date1.getTime() - date.getTime()));
   }
}
При чтении файла размером 1,5 Мб на моем компьютере FileInputStream выполнил работу за ~3500 миллисекунд, а вот BufferedInputStream — за ~1700 миллисекунд. Как видишь, буферизированный поток оптимизировал работу программы в 2 раза! 🙂 Мы еще продолжим изучать классы ввода-вывода — до встречи!

javarush.ru

Java. Система ввода/вывода

Статья проплачена кошками — всемирно известными производителями котят.

Если статья вам понравилась, то можете поддержать проект.

Класс File
Поток
Класс OutputStream
Исключения ввода/вывода

Java имеет в своём составе множество классов, связанных с вводом/выводом данных. Рассмотрим некоторые из них.

Класс File

В отличие от большинства классов ввода/вывода, класс File работает не с потоками, а непосредственно с файлами. Данный класс позволяет получить информацию о файле: права доступа, время и дата создания, путь к каталогу. А также осуществлять навигацию по иерархиям подкаталогов.

Подробнее о классе java.io.File

Поток

При работе с данными ввода/вывода вам будет часто попадаться термин Поток (Stream). Поток — это абстрактное значение источника или приёмника данных, которые способны обрабатывать информацию. Вы в реальности не видите, как действительно идёт обработка данных в устройствах ввода/вывода, так как это сложно и вам это не нужно. Это как с телевизором — вы не знаете, как сигнал из кабеля превращается в картинку на экране, но вполне можете переключаться между каналами через пульт.

Есть два типа потоков: байтовые и символьные. В некоторых ситуациях символьные потоки более эффективны, чем байтовые.

За ввод и вывод отвечают разные классы Java. Классы, производные от базовых классов InputStream или Reader, имеют методы с именами read() для чтения отдельных байтов или массива байтов (отвечают за ввод данных). Классы, производные от классов OutputStream или Write, имеют методы с именами write() для записи одиночных байтов или массива байтов (отвечают за вывод данных).

Подробнее о классе InputStream

Класс OutputStream

Класс OutputStream — это абстрактный класс, определяющий потоковый байтовый вывод.

В этой категории находятся классы, определяющие, куда направляются ваши данные: в массив байтов (но не напрямую в String; предполагается что вы сможете создать их из массива байтов), в файл или канал.

BufferedOutputStream
Буферизированный выходной поток
ByteArrayOutputStream
Создает буфер в памяти. Все данные, посылаемые в этот поток, размещаются в созданном буфере
DataOutputStream
Выходной поток, включающий методы для записи стандартных типов данных Java
FileOutputStream
Отправка данных в файл на диске. Реализация класса OutputStream
ObjectOutputStream
Выходной поток для объектов
PipedOutputStream
Реализует понятие выходного канала.
FilterOutputStream
Абстрактный класс, предоставляющий интерфейс для классов-надстроек, которые добавляют к существующим потокам полезные свойства.

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

  • int close() — закрывает выходной поток. Следующие попытки записи передадут исключение IOException
  • void flush() — финализирует выходное состояние, очищая все буферы вывода
  • abstract void write (int oneByte) — записывает единственный байт в выходной поток
  • void write (byte[] buffer) — записывает полный массив байтов в выходной поток
  • void write (byte[] buffer, int offset, int count) — записывает диапазон из count байт из массива, начиная с смещения offset

BufferedOutputStream

Класс BufferedOutputStream не сильно отличается от класса OutputStream, за исключением дополнительного метода flush(), используемого для обеспечения записи данных в буферизируемый поток. Буферы вывода нужно для повышения производительности.

ByteArrayOutputStream

Класс ByteArrayOutputStream использует байтовый массив в выходном потоке. Метод close() можно не вызывать.

DataOutputStream

Класс DataOutputStream позволяет писать элементарные данные в поток через интерфейс DataOutput, который определяет методы, преобразующие элементарные значения в форму последовательности байтов. Такие потоки облегчают сохранение в файле двоичных данных.

Класс DataOutputStream расширяет класс FilterOutputStream, который в свою очередь, расширяет класс OutputStream.

Методы интерфейса DataOutput:

  • writeDouble(double value)
  • writeBoolean(boolean value)
  • writeInt(int value)

FileOutputStream

Класс FileOutputStream создаёт объект класса OutputStream, который можно использовать для записи байтов в файл. Создание нового объекта не зависит от того, существует ли заданный файл, так как он создаёт его перед открытием. В случае попытки открытия файла, доступного только для чтения, будет передано исключение.

Классы символьных потоков

Символьные потоки имеют два основных абстрактных класса Reader и Writer, управляющие потоками символов Unicode.

Reader

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

  • abstract void close() — закрывает входной поток. Последующие попытки чтения передадут исключение IOException
  • void mark(int readLimit) — помещает метку в текущую позицию во входном потоке
  • boolean markSupported() — возвращает true, если поток поддерживает методы mark() и reset()
  • int read() — возвращает целочисленное представление следующего доступного символа вызывающего входного потока. При достижении конца файла возвращает значение -1. Есть и другие перегруженные версии метода
  • boolean ready() — возвращает значение true, если следующий запрос не будет ожидать.
  • void reset() — сбрасывает указатель ввода в ранее установленную позицию метки
  • logn skip(long charCount) — пропускает указанное число символов ввода, возвращая количество действительно пропущенных символов
BufferedReader
Буферизированный входной символьный поток
CharArrayReader
Входной поток, который читает из символьного массива
FileReader
Входной поток, читающий файл
FilterReader
Фильтрующий читатель
InputStreamReader
Входной поток, транслирующий байты в символы
LineNumberReader
Входной поток, подсчитывающий строки
PipedReader
Входной канал
PushbackReader
Входной поток, позволяющий возвращать символы обратно в поток
Reader
Абстрактный класс, описывающий символьный ввод
StringReader
Входной поток, читающий из строки

Класс BufferedReader

Класс BufferedReader увеличивает производительность за счёт буферизации ввода.

Класс CharArrayReader

Класс CharArrayReader использует символьный массив в качестве источника.

Класс FileReader

Класс FileReader, производный от класса Reader, можно использовать для чтения содержимого файла. В конструкторе класса нужно указать либо путь к файлу, либо объект типа File.

Writer

Класс Writer — абстрактный класс, определяющий символьный потоковый вывод. В случае ошибок все методы класса передают исключение IOException.

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

  • Writer append(char c) — добавляет символ в конец вызывающего выходного потока. Возвращает ссылку на вызывающий поток
  • Writer append(CharSequence csq) — добавляет символы в конец вызывающего выходного потока. Возвращает ссылку на вызывающий поток
  • Writer append(CharSequence csq, int start, int end) — добавляет диапазон символов в конец вызывающего выходного потока. Возвращает ссылку на вызывающий поток
  • abstract void close() — закрывает вызывающий поток
  • abstract void flush() — финализирует выходное состояние так, что все буферы очищаются
  • void write(int oneChar) — записывает единственный символ в вызывающий выходной поток. Есть и другие перегруженные версии метода
BufferedWriter
Буферизированный выходной символьный поток
CharArrayWriter
Выходной поток, который пишет в символьный массив
FileWriter
Выходной поток, пишущий в файл
FilterWriter
Фильтрующий писатель
OutputStreamWriter
Выходной поток, транслирующий байты в символы
PipedWriter
Выходной канал
PrintWriter
Выходной поток, включающий методы print() и println()
StringWriter
Выходной поток, пишущий в строку
Writer
Абстрактный класс, описывающий символьный вывод

Класс BufferedWriter

Класс BufferedWriter — это класс, производный от класса Writer,который буферизует вывод. С его помощью можно повысить производительность за счёт снижения количества операций физической записи в выходное устройство.

Класс CharArrayWriter

Класс CharArrayWriter использует массив для выходного потока.

Класс FileWriter

Класс FileWriter создаёт объект класса, производного от класса Writer, который вы можете применять для записи файла. Есть конструкторы, которые позволяют добавить вывод в конец файла. Создание объекта не зависит от наличия файла, он будет создан в случае необходимости. Если файл существует и он доступен только для чтения, то передаётся исключение IOException.

Чтение и запись файлов

Существует множество классов и методов для чтения и записи файлов. Наиболее распространённые из них — классы FileInputStream и FileOutputStream, которые создают байтовые потоки, связанные с файлами. Чтобы открыть файл, нужно создать объект одного из этих файлов, указав имя файла в качестве аргумента конструктора.


FileInputStream(String filename) throws FileNotFoundException
FileOutputStream(String filename) throws FileNotFoundException

В filename нужно указать имя файла, который вы хотите открыть. Если при создании входного потока файл не существует, передаётся исключение FileNotFoundException. Аналогично для выходных потоков, если файл не может быть открыт или создан, также передаётся исключение. Сам класс исключения происходит от класса IOException. Когда выходной файл открыт, любой ранее существовавший файл с тем же именем уничтожается.

После завершения работы с файлом, его необходимо закрыть с помощью метода close() для освобождения системных ресурсов. Незакрытый файл приводит к утечке памяти.

В JDK 7 метод close() определяется интерфейсом AutoCloseable и можно явно не закрывать файл, а использовать новый оператор try-с-ресурсами, что для Android пока не слишком актуально.

Чтобы читать файл, нужно вызвать метод read(). Когда вызывается этот метод, он читает единственный байт из файла и возвращает его как целое число. Когда будет достигнут конец файла, то метод вернёт значение -1. Примеры использования методов есть в различных статьях на сайте.

Иногда используют вариант, когда метод close() помещается в блок finally. При таком подходе все методы, которые получают доступ к файлу, содержатся в пределах блока try, а блок finally используется для закрытия файла. Таким образом, независимо от того, как закончится блок try, файл будет закрыт.

Так как исключение FileNotFoundException является подклассом IOException, то не обязательно обрабатывать два исключения отдельно, а оставить только IOException, если вам не нужно отдельно обрабатывать разные причины неудачного открытия файла. Например, если пользователь вводит вручную имя файла, то более конкретное исключение будет к месту.

Для записи в файл используется метод write().


void write(int value) throws IOException

Метод пишет в файл байт, переданный параметром value. Хотя параметр объявлена как целочисленный, в файл записываются только младшие восемь бит. При ошибке записи передаётся исключение.

В JDK 7 есть способ автоматического управления ресурсами:


try (спецификация_ресурса) {
    // использование ресурса
}

Когда в Android будет полноценная поддержка JDK 7, то дополним материал.

Буферизированное чтение из файла — BufferedReader

Чтобы открыть файл для посимвольного чтения, используется класс FileInputReader; имя файла задаётся в виде строки (String) или объекта File. Ускорить процесс чтения помогает буферизация ввода, для этого полученная ссылка передаётся в конструктор класса BufferedReader. Так как в интерфейсе класса имеется метод readLine(), все необходимое для чтения имеется в вашем распоряжении. При достижении конца файла метод readLine() возвращает ссылку null.

Вывод в файл — FileWriter

Объект FileWriter записывает данные в файл. При вводе/выводе практически всегда применяется буферизация, поэтому используется BufferedWriter.

Когда данные входного потока исчерпываются, метод readLine() возвращает null. Для потока явно вызывается метод close(); если не вызвать его для всех выходных файловых потоков, в буферах могут остаться данные, и файл получится неполным.

Сохранение и восстановление данных — PrintWriter

PrintWriter форматирует данные так, чтобы их мог прочитать человек. Однако для вывода информации, предназначенной для другого потока, следует использовать классы DataOutputStream для записи данных и DataInputStream для чтения данных.

Единственным надежным способом записать в поток DataOutputStream строку так, чтобы ее можно было потом правильно считать потоком DataInputStream, является кодирование UTF-8, реализуемое методами readUTF() и writeUTF(). Эти методы позволяют смешивать строки и другие типы данных, записываемые потоком DataOutputStream, так как вы знаете, что строки будут правильно сохранены в Юникоде и их будет просто воспроизвести потоком DataInputStream.

Метод writeDouble() записывает число double в поток, а соответствующий ему метод readDouble() затем восстанавливает его (для других типов также существуют подобные методы).

RandomAccessFile — Чтение/запись файлов с произвольным доступом

Работа с классом RandomAccessFile напоминает использование совмещенных в одном классе потоков DataInputStream и DataOutputStream (они реализуют те же интерфейсы DataInput и DataOutput). Кроме того, метод seek() позволяет переместиться к определенной позиции и изменить хранящееся там значение.

При использовании RandomAccessFile необходимо знать структуру файла. Класс RandomAccessFile содержит методы для чтения и записи примитивов и строк UTF-8.

RandomAccessFile может открываться в режиме чтения («r») или чтения/записи («rw»). Также есть режим «rws», когда файл открывается для операций чтения-записи и каждое изменение данных файла немедленно записывается на физическое устройство.

Исключения ввода/вывода

В большинстве случаев у классов ввода/вывода используется исключение IOException. Второе исключение FileNotFoundException передаётся в тех случаях, когад файл не может быть открыт. Данное исключение происходит от IOException, поэтому оба исключения можно обрабатывать в одном блоке catch, если у вас нет нужды обрабатывать их по отдельности.

Дополнительное чтение

Используем AsyncTask для загрузки текстового файла из сети — используются BufferedReader, InputStreamReader, InputStream.

Реклама

developer.alexanderklimov.ru

Курс Java Core — Лекция: Потоки для ввода/вывода файлов

— А начнем мы с потоков для ввода/вывода файлов. Но обо всем по порядку.

Для чтений и записи файлов есть два класса:  FileInputStream и FileOutputStream. Как ты уже, наверное, догадался,  FileInputStream позволяет последовательно читать из файла байты, а FileOutputStream – записывать в файл байты. Вот какие методы есть у этих классов:

МетодЧто метод делает
FileInputStream(String fileName);
— это конструктор. Позволяет указать имя файла на диске, из которого созданный объект будет читать данные.
int read();
— метод читает один байт из файла и возвращает его как результат. Тип результата расширяется до int.
int available();
— метод возвращает количество непрочитанных (доступных) байт.
void close();
— метод «закрывает» поток, вызывается после окончания работы с потоком.
Объект выполняет служебные операции, связанные с закрытием файла на диске и т.д.
Из потока больше нельзя читать данные.

Давай ради интереса посчитаем сумму всех байт в файле на диске. Вот как будет выглядеть этот код:

Подсчет суммы всех байт файла на диске

public static void main(String[] args) throws Exception
{
 
 FileInputStream inputStream = new FileInputStream("c:/data.txt");
 long sum = 0;

 while (inputStream.available() > 0) 
 {
  int data = inputStream.read(); 
  sum += data; 
 }
 inputStream.close(); 

 System.out.println(sum); 
}

— Мы уже раньше что-то подобное разбирали. А как устроен FileOutputStream?

— Ок. Вот, смотри:

МетодЧто метод делает
FileOutputStream (String fileName);

— это конструктор. Позволяет указать имя файла на диске, в который созданный объект будет писать данные.

void write(int data);

— метод записывает очередной байт, обрезая переменную data до одного байта.

void flush();

— часто данные для записи сначала собираются в большие блоки в памяти, а потом только пишутся на диск.

Команда flush требует немедленно записать всю несохраненную информацию на диск.

void close();

— метод «закрывает» поток, вызывается после окончания работы с потоком.

Объект выполняет служебные операции, связанные с закрытием файла на диске и т.д.

В поток больше нельзя писать данные, flush при этом вызывается автоматически.

— И все?

— Да, тут фактически только один метод для записи – write, который записывает только один байт за раз. Но благодаря ему можно записать в файл сколько угодно информации.

Программирование – это процесс разбиения одной большой и сложной задачи на много маленьких. Тут происходит практически тот же процесс: чтение и запись больших данных маленькими порциями – по кусочкам – по одному байту.

Вот как можно скопировать файл на диске, пользуясь этими классами:

Копируем файл на диске

public static void main(String[] args) throws Exception
{
 
 FileInputStream inputStream = new FileInputStream("c:/data.txt");
 
 FileOutputStream outputStream = new FileOutputStream("c:/result.txt");

 while (inputStream.available() > 0) 
 {
  int data = inputStream.read(); 
  outputStream.write(data); 
 }

 inputStream.close(); 
 outputStream.close();
}

— Спасибо, Риша. Наконец-то понял, как на самом деле работает этот код.

javarush.ru

Курс Java Core — Лекция: Потоки ввода/вывода

— Привет, Амиго! Сегодня мы будет знакомиться с потоками ввода-вывода. Пару дней назад мы немного цепляли данную тему, но сегодня пройдемся по ней основательно. Потоки ввода-вывода делятся на 4 категории:

1) Потоки делятся по направлению: потоки ввода и потоки вывода

2) Потоки делятся по типу данных: работают с байтами или работают с символами.

Таблица:

Поток вводаПоток вывода
Работает с байтамиInputStreamOutputStream
Работает с символамиReaderWriter

Если объект реализует интерфейс InputStream, значит, он поддерживает последовательное чтение из него байт (byte).

Если объект реализует интерфейс OutputStream, значит, он поддерживает последовательную запись в него байт (byte).

Если объект реализует интерфейс Reader, значит, он поддерживает последовательное чтение из него символов (char).

Если объект реализует интерфейс Writer, значит, он поддерживает последовательную запись в него символов (char).

Поток вывода напоминает принтер. На принтер мы можем выводить документы. В поток вывода мы можем выводить данные.

Тогда поток ввода можно сравнить со сканером, ну или с розеткой. С помощью сканера мы можем ввести документы к себе в компьютер. Также мы можем подключится к розетке и получать из нее электричество. Из потока ввода мы можем получать данные.

— А где они используются?

— Эти классы используются в Java повсеместно. Известный нам System.in – это статическая переменная по имени in типа InputStream в классе System.

— Надо же! Оказывается, все это время я пользовался потоком InputStream и не знал об этом. System.out – тоже поток?

— Да, System.out – это статическая переменная по имени out типа PrintStream (наследник OutputStream) в классе System.

— Т.е. я все время пользовался потоками и даже не подозревал об этом?

— Да, и это говорит лишь о том, насколько такие потоки удобны. Просто берешь и пользуешься.

— Хотя этого нельзя сказать про System.in. К нему постоянно приходилось добавлять BufferedReader и InputStreamReader.

— Да, это так. Но на это тоже были свои причины.

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

Зато их можно соединить последовательно и получить очень сложную функциональность, если она тебе понадобилась. Смотри пример:

Вывод строки на консоль

System.out.println("Hello");

Сохранили поток вывода на консоль в отдельную переменную.
Выводим в поток строку.

PrintStream console = System.out;
console.println("Hello");

Создали динамический (растягивающийся) массив байт в памяти.
Связали его с новым потоком вывода – объектов PrintStream
Выводим в поток строку.

ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintStream console = new PrintStream(stream);
console.println("Hello");

— Действительно, чем-то похоже на конструктор Lego. Только непонятно, что весь этот код делает.

— Пусть это тебя не беспокоит сейчас. Всему свое время.

Хочу, чтобы ты запомнил вот что: если класс реализует интерфейс OutputStream – он позволяет записывать в него байты. Почти так же, как ты выводишь данные на консоль. Что он будет с этими данными делать – его задача. В «конструкторе» важно не назначение отдельного элемента, а насколько классные вещи мы можем собрать, благодаря многообразию существующих элементов.

— Хорошо. Тогда с чего мы начнем?

javarush.ru

Java 8 потоки ввода/вывода — urvanov.ru

Цикл статей «Учебник Java 8».

Следующая статья — «Java 8 сериализация».
Предыдущая статья — «Java 8 исключения».

Содержание

Введение

Потоки байт

— Диаграмма классов, показывающая иерархию основных дочерних классов для класса java.io.InputStream

— Диаграмма классов, показывающая иерархию основных дочерних классов для класса java.io.OutputStream

— java.io.InputStream

— java.io.OutputStream

— java.io.FileInputStream и java.io.FileOutputStream

— java.io.ByteArrayInputStream и java.io.ByteArrayOutputStream

— java.io.FilterInputStream и java.io.FilterOutputStream

— java.io.DataInputStream и java.io.DataOutputStream

— java.io.BufferedInputStream и java.io.BufferedOutputStream

— java.io.PipedInputStream и java.io.PipedOutputStream

— java.io.ObjectInputStream и java.io.ObjectOutputStream

Потоки символов

— Диаграмма классов, показывающая иерархию основных дочерних классов для java.io.Reader

— Диаграмма классов, показывающая иерархию основных дочерних классов для java.io.Writer

— java.util.Scanner и java.io.PrintStream

Введение

Поток ввода/вывода (I/O Stream) представляет собой источник данных или место их назначения. Потоки могут представлять собой абсолютно различные источники и места назначения: файлы на диска, устройства, сеть, другие программы, массивы в памяти т. д.

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

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

Потоки байт

Все классы, работающие с потоками байт, наследуются от абстрактных классов java.io.InputStream  или java.io.OutputStream.

Диаграмма классов, показывающая иерархию основных дочерних классов для класса java.io.InputStream

Диаграмма классов, показывающая иерархию основных дочерних классов для класса java.io.OutputStream


Принцип работы с каждым из этих классов весьма схож. Важно знать методы java.io.InputStream  и java.io.OutputStream, поскольку они наследуются в каждом из этих классов.

java.io.InputStream

Абстрактный класс, являющийся базовым классом для всех классов, представляющий поток ввода.

Основные методы:

public int available() throws IOException

public int available()

              throws IOException

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

public void close() throws IOException

public void close()

           throws IOException

Закрывает поток и освобождает все ресурсы.

public void mark(int readlimit)

public void mark(int readlimit)

Помечает текущую позицию во входной строке. Работает только если markSupported()  возвращает true. Смысл этого метода в том, что поток каким-нибудь образом запоминает все считанные после вызова этого метода данные и может вернуть те же самые данные ещё раз после вызова метода reset(). Если после вызова метода mark(int readLimit)  из потока было прочитано больше readLimit  байт, то поток не обязан запоминать что бы то ни было.

public void reset() throws IOException

public void reset()

           throws IOException

Если метод markSupported()  возвращает true, то:

  • Если метод mark()  не был вызван ни разу, либо количество байт, которые были прочитаны из потока после вызова mark() , больше аргумента метода mark()  в последнем его вызове, то может броситься исключение IOException.
  • Если исключение IOException  не было брошено, то поток возвращается в такое состояние, что все вызовы методов read()  в дальнейшем будут возвращать те же данные, которые они возвращали с момента последнего вызова метода mark()  (либо с начала потока, если метод mark()  не был вызван ни разу).

Если метод markSupported()  возвращает false, то:

  • Вызов метода reset()  может бросить исключение IOException.
  • Если не бросается исключение IOException, то поток сбрасывается в фиксированное состояние, которое зависит от конкретного типа входного потока, и как он был создан. Байты, которые будут прочитаны при последующих вызовах методов read(),  зависят от конкретного типа входной строки.

 

public boolean markSupported()

public boolean markSupported()

Возвращает true, если реализация InputStream  поддерживает методы mark() и reset().

public abstract int read() throws IOException

public abstract int read()

                  throws IOException

Считывает один байт из потока. Возвращает его в int, содержащем значение от 0 до 255. Возвращает -1, если достигли конца потока. Блокирует выполнение текущего потока программы до тех пор, пока не появятся входные данные, не достигнется конец потока, либо бросится исключение.

urvanov.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *