input output stream

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

Продолжаем изучать стандартную библиотеку 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.         RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");//создаем екземпляр класса RandomAccessFile
  14.         //в конструктор передаем файл и строку "r" которая указывает, что файл можно только читать
  15.         StringBuilder sb = new StringBuilder();//нам нужно будет конкатенировать много строк
  16.         //String может значительно потребить ресурсы
  17.         int b = randomAccessFile.read();//читаем поток байтов
  18.         while(b != -1){//-1 будет означать конец файла. Если не писать этой строки, то прочитается только первая строка файла
  19.             sb = sb.append((char)b);//добавляем к нашей строке поток байтов, который мы прикастили к char. Если не кастить
  20.             //то выведет цифры, байтов
  21.             b = randomAccessFile.read();//читаем файл и записываем в поток
  22.         }
  23.         randomAccessFile.close();//не забываем закрывать поток
  24.         System.out.println(sb);//вывод в консоль
  25.  
  26.     }
  27.    
  28.     public static void writeFile(String data) throws IOException{
  29.         File file = new File("test.txt");//файл находится в нашем проекте
  30.         RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");//"rw" означает, что файл можно записать
  31.         randomAccessFile.write(data.getBytes());//так как мы не можем работать с символами напрямую, нужно перевести строку в байты
  32.         randomAccessFile.close();//закрываем поток
  33.     }
  34.  
  35.     public static void main(String[] args) throws IOException {
  36.         ReadAndWriteFileExample.writeFile("Some my data");
  37.         ReadAndWriteFileExample.readFile();
  38.  
  39.     }
  40.  
  41. }

 

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

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

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

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

класс ридер

класс райтер

Вот пример чтения и записи файла используя BufferedReader и BufferedWriter:

Код    
  1. package com.java_master;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.BufferedWriter;
  5. import java.io.FileInputStream;
  6. import java.io.FileOutputStream;
  7. import java.io.IOException;
  8. import java.io.InputStreamReader;
  9. import java.io.OutputStreamWriter;
  10. import java.io.PrintWriter;
  11. import java.io.StringReader;
  12.  
  13. public class ReadWriteFileUsingBuffered {
  14.  
  15.     public static void readFile() throws IOException{
  16.          FileInputStream fileInputStream = new FileInputStream("test.txt");
  17.             InputStreamReader input = new InputStreamReader(fileInputStream);
  18.             BufferedReader br = new BufferedReader(input);
  19.             StringBuilder sb = new StringBuilder();
  20.             String line = br.readLine();
  21.             while (line != null) {
  22.                 sb.append(line);
  23.                 line = br.readLine();
  24.             }
  25.  
  26.             System.out.println(sb);
  27.             fileInputStream.close();
  28.             input.close();
  29.             br.close();
  30.     }
  31.    
  32.     public static void writeFile(String data) throws IOException{
  33.         FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
  34.         OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
  35.         BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
  36.         bufferedWriter.write(data);
  37.         bufferedWriter.flush();
  38.         bufferedWriter.close();
  39.        
  40.     }
  41.    
  42.     public static void writeFileSimpleMethod(String data) throws IOException {
  43.         PrintWriter writer = new PrintWriter("test.txt");
  44.         writer.println(data);
  45.         writer.close();
  46.     }
  47.    
  48.     public static void main(String[] args) throws IOException{
  49.         ReadWriteFileUsingBuffered.writeFile("SOme data");
  50.         ReadWriteFileUsingBuffered.writeFileSimpleMethod("Sm=i");
  51.         ReadWriteFileUsingBuffered.readFile();
  52.     }
  53. }

Читать и записывать можно не только текстовый файл. Java позволяет Вам работать и создавать множество типов данных. Только работая с сетевыми данными нужно помнить о сериализации. Сериализация — это процесс хранения состояния экземпляра как поток байтов. Подробнее мы будет рассматривать сериализацию когда будем работать с сетью.

Теперь настало время рассмотреть класс System. Тот самый класс, который с первых уроков помогал нам выводить данные в консоль. Класс System содержит поле in, которое является ссылкой на объект класса InputStream, и поля out, err — ссылка на объект класса PrintStream, который объявлен с модификаторами public. Эти потоки связаны с консолью, но могут использоваться для вывода на другое устройство. Как пользоваться классом System Вы уже знаете, но мы выделим отдельную статью для форматного вывода. Там Вы узнаете новые возможности класса System.

Что касается класса Scanner. Это простой текстовый сканер, который может анализировать примитивные типы и строки, используя регулярные выражения. С его применением мы уже знакомы. Scanner может не только читать ввод с консоли. Им также можно читать файлы:

Код    
  1. package com.java_master;
  2.  
  3. import java.io.File;
  4. import java.io.FileNotFoundException;
  5. import java.util.Scanner;
  6.  
  7. public class ScannerExampleClass {
  8.    
  9.     public static void readFile() throws FileNotFoundException{
  10.         File file = new File("test.txt");
  11.         Scanner scanner = new Scanner(file);
  12.        
  13.         StringBuilder sb = new StringBuilder();
  14.         while(scanner.hasNextLine()){
  15.             sb.append(scanner.nextLine());
  16.         }
  17.         System.out.println(sb);
  18.     }
  19.    
  20.     public static void readFromConsole(){
  21.         Scanner scanner = new Scanner(System.in);
  22.         int variable = scanner.nextInt();
  23.         System.out.println(variable);
  24.     }
  25.    
  26.     public static void main(String[] args) throws FileNotFoundException{
  27.         ScannerExampleClass.readFile();
  28.         ScannerExampleClass.readFromConsole();
  29.     }
  30.  
  31. }

Ну вот, пожалуй, и все что касается системы ввода/вывода и работы с файлами. Это не единственные но самые распространенные способы чтения/записи.

 

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