Свой файловый поток java

Потоки для ввода/вывода файлов

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

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

FileInputStream(String fileName);

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

public static void main(String[] args) throws Exception < //создаем объект FileInputStream, привязанный к файлу «c:/data.txt». 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);

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

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

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

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

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

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

В поток больше нельзя писать данные, 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(); // прочитать очередной байт в переменную data outputStream.write(data); // и записать его во второй поток > inputStream.close(); //закрываем оба потока. Они больше не нужны. outputStream.close(); >

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

Java-университет

В Syntax бы так объясняли. Хотя, возможно, сейчас всё понятно, т.к. смог пролезть через тернии непонимания.

Сделал так, программка копирует файл, замеряет сколько времени заняло копирование, скорость копирования в байт/с и отображает размер файла. Можно поиграться и наглядно посмотреть разницу между буферизированным вводом/выводом и без. Листинг программы CopyFile (начало):

 package com.javalerning.example.iostreams.myownexample01; import java.io.*; public class CopyFile < public static void main(String[] args) < try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) < System.out.print("Введите полный путь к файлу, который хотите скопировать: "); String sourceFilePath = reader.readLine(); System.out.print("Введите полный путь и название файла, в который сохранить копию: "); String destinationFilePath = reader.readLine(); File sourceFile = new File(sourceFilePath); if (!sourceFile.exists() || !sourceFile.canRead()) < //если заданный путь к файлу не существует или файл невозможно прочесть. System.out.println("Файл не существует или недоступен для чтения."); return; >File destinationFile = new File(destinationFilePath); if (destinationFile.exists()) < //если файл уже существует вызываем диалог переписать ли его. System.out.println("Такой файл уже существует. Вы уверены что хотите перезаписать его? \"Y/N\""); boolean isCancel = false; while (!isCancel) < switch (reader.readLine()) < case "Y": isCancel = true; break; case "N": System.out.println("Копирование отменено!"); return; default: break; >> > 

Давно мучает вопрос оптимизации: зачем определять переменную в цикле? Будет ли оптимальнее определить ее перед циклом?

https://stackoverflow.com/questions/5155226/fileinputstream-vs-filereader Чтобы полностью понять это, вам нужно понять, что такое поток символов и байтов, поэтому давайте быстро взглянем на это— Потоки байтов Поток байтов обращается к файлу байт за байтом. Программы Java используют потоки байтов для ввода и вывода 8-битных байтов. Он подходит для любого типа файлов, однако не совсем подходит для текстовых файлов. Например, если файл использует кодировку unicode и символ представлен двумя байтами, поток байтов будет обрабатывать их отдельно, и вам нужно будет выполнить преобразование самостоятельно. Байт-ориентированные потоки не используют никакой схемы кодирования, в то время как символьно-ориентированные потоки используют схему кодирования символов (UNICODE). Все классы потока байтов происходят от InputStream и OutputStream . Поток символов Символьный поток будет считывать файл посимвольно. Символьный поток — это концепция более высокого уровня, чем поток байтов . Поток символов — это, по сути, поток байтов, который был обернут логикой, позволяющей ему выводить символы из определенной кодировки . Это означает, что потоку символов необходимо присвоить кодировку файла для правильной работы. Поток символов может поддерживать все типы наборов символов ASCII, Unicode, UTF-8, UTF-16 и т.д. Все классы символьного потока происходят от Reader и Writer. Если вы попытаетесь прочитать из .txtфайла, который был записан в кодировке Uni-8, которая по умолчанию используется в java, то чтение файла с помощью классов Reader и InputStream даст одинаковый результат.Так как здесь каждый байт представляет один символ. Я создал несколько методов, которые помогут вам понять разницу между этими двумя терминами —

 FileInputStream reads byte by byte и FileReader reads char by char. 

Если у кого-то возник вопрос, как у меня, можно ли измерить размер файла (в байтах) приведённым в статье кодом, ответ — нет. Ниже код из статьи, о которой идёт речь:

 public static void main(String[] args) throws Exception < //создаем объект FileInputStream, привязанный к файлу «c:/data.txt». 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); //выводим сумму на экран. > 

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

 import java.io.FileInputStream; import java.io.IOException; public class FileSize < public static void main(String[] args) throws IOException < FileInputStream inputStream = new FileInputStream("C:\\1.png"); long bytes = inputStream.available(); // Получаем размер в байтах inputStream.close(); long kB = bytes / 1024; // Получаем размер в килобайтах long mB = kB / 1024; // Получаем размер в мегабайтах System.out.println(bytes + " bytes"); System.out.println(kB + " Kb"); System.out.println(mB + " Mb"); >> 

Источник

Потоки ввода-вывода. Работа с файлами

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

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

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

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

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

Но поскольку работать с байтами не очень удобно, то для работы с потоками символов были добавлены абстрактные классы Reader (для чтения потоков символов) и Writer (для записи потоков символов).

Все остальные классы, работающие с потоками, являются наследниками этих абстрактных классов. Основные классы потоков:

Классы потоков в Java

Потоки байтов

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

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

Источник

Читайте также:  Php array get number
Оцените статью