Введення і виведення даних мовою Java: консоль і файли
Зміст

У мові програмування Java введення/виведення інформації побудовано на понятті потоку.

Потікце абстрактне поняття для позначення джерела чи приймача даних, що може передавати або отримувати дані.

Для використання класу потоку введення/виведення потрібно імпортувати пакет java.io:

import java.io.*;

У мові Java розрізняють два види потоків:

Байтові потоки введення/виведення поділяють на нащадків таких класів:

InputStreamабстрактний клас, що описує байтовий потік введення.

Нащадки InputStream

  • BufferedInputStream — опис буферизованого потоку введення;
  • ByteArrayInputStream — опис потоку введення, який читає байти з масиву;
  • DataInputStream — зчитування даних стандартних типів;
  • FileInputStream — зчитування даних з файлу;
  • FilterInputStream — реалізація абстрактного класу InputStream;
  • ObjectInputStream — введення об’єктів;
  • PipedInputStream — опис каналу введення;
  • PushbackInputStream — підтримка повернення 1 байту у потік введення;
  • SequenceInputStream — потік з кількох потоків, дані з яких читають по черзі.

Основні методи класу InputStream:

OutputStreamабстрактний клас, що описує байтовий потік виведення.

Нащадки OutputStream

  • BufferedOutputStream — опис буферизованого потоку виведення;
  • ByteArrayOutputStream — запис байтів у масив;
  • DataOutputStream — містить методи читання даних стандартних типів;
  • FileOutputStream — запис даних у файл;
  • FilterOutputStream — реалізація абстрактного класу OutputStream;
  • ObjectOutputStream — виведення об’єктів;
  • PipedOutputStream — опис каналу виведення;
  • PrintStream — містить методи виведення у консоль (інтерфейс командного рядка):
    • print — без додавання ознаки кінця рядка в кінці виведення;
    • println — з додаванням ознаки кінця рядка в кінці виведення.

Основні методи класу OutputStream:

Символьні потоки введення/виведення поділяють на нащадків таких класів:

Readerабстрактний клас, що описує потік введення символів.

Нащадки Reader:

  • BufferedReader — буферизований потік введення символів;
  • CharArrayReader — зчитування символів з масиву;
  • FileReader — зчитування символів з файлу;
  • FilterReader — фільтрований потік читання;
  • InputStreamReader — без буферизації з перетворенням байтів у символи;
  • LineNumberReader — введення з підрахунком рядків;
  • PipedReader — опис каналу введення;
  • PushbackReader — можливість повертати символи назад у потік ввиведення;
  • StringReader — введення зі зчитуванням символів з рядка.

Основні методи класу Reader:

Writerабстрактний клас, що описує потік виведення символів.

Нащадки Writer:

  • BufferedWriter — буферизований потік виведення символів;
  • CharArrayWriter — запис символів у масив;
  • FileWriter — запис символів у файл;
  • FilterWriter — фільтрований потік запису;
  • OutputStreamWriter — перетворення символів у байти;
  • PipedWriter — опис каналу виведення;
  • StringWriter — запис символів у рядок.

Основні методи класу Writer:

Стандартні потоки введення/виведення втілені у пакунку java.lang. Цей пакунок імпортується автоматично, тобто не обов’язково використовувати вказівку import. Базовим класом цього пакунку є клас System з трьома змінними, які є посиланнями на стандартні (наперед визначені) потоки введення/виведення:

— див. приклад коду:

package work;
import java.io.*;
import java.util.Scanner;
public class Work
{ public static void main(String[] args) throws IOException
  { Scanner     s = new Scanner(System.in);
    InputStream i = System.in; 
    PrintStream o = System.out;
    PrintStream e = System.err;

    o.println("Введіть літеру латиниці, цифру або знак пунктуації");
    int code = i.read();
    o.println("Код введеного символа "+code);

    o.println("Введіть ціле число ");
    int n = s.nextInt();
    o.println("Було введено число "+n);
    e.println("Жодних проблем!");

    char a[] = new char[5];
    InputStreamReader isr = new InputStreamReader(System.in);
    o.println("Введіть символи (до 5):");
    n = isr.read(a, 1, 4);
    o.println("Кількість введених символів разом з ознакою кінця рядка: " + n);
    for (int j=0; j<a.length; j++)
    o.println("a[" + j + "] = " + a[j]);
  }
}

з таким результатом виконання у консолі:

Введіть літеру латиниці, цифру або знак пункткації
*
Код введеного символа 42
Введіть ціле число 
22
Було введено число 22
Жодних проблем!
Введіть символи (до 5):
qw
Кількість введених символів: 3
a[0] =
a[1] = q
a[2] = w
a[3] = 

a[4] =

Клас Scanner використовують для аналізу і введення даних потоку введення.

Наприклад, для введення цілого числа (не символа, не рядка), як це зроблено у поданому вище прикладі коду.

Методи класу Scanner

Тип
результату
повертання
Назва методу
і тип аргумента
(в дужках)
Призначення (що робить?)
void close() Закриває об'єкт сканера
Pattern delimiter() Повертає шаблон, який об'єкт Scanner в даний час використовує для порівняння роздільників.
String findInLine (Pattern) Повертає об'єкт String, який задовольняє вказаному шаблону.
String findInLine (String) Шукає наступне входження шаблону, створеного з указаного рядка, при незтуванні роздільників.
String findWithinHorizon
(Pattern, int)
Шукає наступне входження вказаного шаблону.
String findWithinHorizon
(String, int)
Шукає наступне входження шаблону введення, ігноруючи роздільник
boolean hasNext() Повертає true, якщо у цього сканера є дані на вході, інакше повертає false.
boolean hasNext(Pattern) Повертає true, якщо наступні повні дані відповідають вказаному шаблону, інакше повертає false.
boolean hasNext(String) Повертає true, якщо наступні дані відповідають шаблону, створеному з вказаного рядка, інакше повертає false.
boolean hasNextBigDecimal() Повертає true, якщо наступні дані можна тлумачити як BigDecimal за допомогою методу nextBigDecimal, інакше повертає false.
boolean hasNextBigInteger() Повертає true, якщо наступні дані можна тлумачити як BigInteger за допомогою методу nextBigInteger, інакше повертає false.
boolean hasNextBigInteger(int) Аналог попереднього методу для вказаної основи.
boolean hasNextBoolean() Повертає true, якщо наступні дані потоку мають логічний тип, інакше повертає false.
boolean hasNextByte() Повертає значення true, якщо наступний байт можна перетворити у тип даних байта, інакше повертає false.
boolean hasNextByte(int)Аналог попереднього методу для вказаної основи.
boolean hasNextDouble() Повертає значення true, якщо наступні дані на вході можна тлумачити як значення типу Double за допомогою методу nextDouble, інакше повертає false.
boolean hasNextFloat() Повертає значення true, якщо наступні дані на вході можна тлумачити як значення типу float за допомогою методу nextFloat, інакше повертає false.
boolean hasNextInt() Повертає значення true, якщо наступні дані на вході можна тлумачити як значення типу int за допомогою методу nextInt, інакше повертає false.
boolean hasNextInt(int) Аналог попереднього методу при вказаній основі числення.
boolean hasNextLine() Повертає логічний тип даних, який відповідає новому рядку String, яку містить об'єкт Scanner.
boolean hasNextLong() Повертає true, якщо наступні дані на вході сканера можна тлумачити як значення типу long з використанням методу nextLong, інакше повертає false.
boolean hasNextLong(int) Аналог попереднього методу при вказаній основі числення.
boolean hasNextShort() Повертає true, якщо наступні дані на вході сканера можна тлумачити як значення типу Short з використанням методу nextShort, інакше повертає false.
boolean hasNextShort(int) Аналог попереднього методу при вказаній основі числення.
IOException ioException() Повертає IOException, останній раз виданий в основі сканера Readable.
Locale locale() Повертає локаль.
MatchResult match() Повертає результат останньої дії з об'єктом.
String next() Сканує і повертає наступну повну частину даних.
String next (Pattern) Сканує і повертає наступну повну частину даних, якщо вона відповідає вказаному шаблону.
String next (String) Сканує і повертає наступну повну частину даних, якщо вона відповідає вказаному шаблону, створеному із зазначеного рядка.
BigDecimalnextBigDecimal()Сканує і повертає наступну повну частину даних як BigDecimal.
BigIntegernextBigInteger()Сканує і повертає наступну повну частину даних як BigInteger.
BigIntegernextBigInteger(int)Аналог попереднього методу при вказаній основі числення.
boolean nextBoolean() Сканує і повертає наступну частину даних як логічне значення.
byte nextByte() Сканує і повертає наступну частину даних як тип byte.
byte nextByte(int) Аналог попереднього методу при вказаній основі числення.
doublenextDouble() Сканує і повертає наступну частину даних як тип double.
float nextFloat() Сканує і повертає наступну частину даних як тип float.
int nextInt() Сканує і повертає наступну частину даних як тип int.
int nextInt(int) Аналог попереднього методу при вказаній основі числення.
StringnextLine() Переміщує сканер за поточний рядок і повертає пройдені дані.
long nextLong() Сканує і повертає наступну частину даних як тип long.
long nextLong(int)Аналог попереднього методу при вказаній основі числення.
shortnextShort() Сканує і повертає наступну частину даних як тип short.
short nextShort(int)Аналог попереднього методу при вказаній основі числення.
int radix() Повертає поточну основу числення.
void remove() Вилучає поточний об'єкт.
Scanner reset () Скидає налаштування локалі, радіуса і роздільника.
Scanner skip (Pattern) Пропускає введення, що відповідає вказаному шаблону, нехтуючи роздільниками.
Scanner skip (String) Пропускає введення, що відповідає шаблону, створеному із зазначеного рядка.
String toString() Повертає рядкове подання.
Scanner useDelimiter (Pattern) Встановлює шаблон обмеження поточного сканера за вказаним шаблоном.
Scanner useDelimiter (String) Аналог попереднього методу, але за шаблоном, створеним ызз зазначеного рядка.
Scanner useLocale (Locale) Встановлює вказану локалізацію.
Scanner useRadix (int) Встановлює вказану основу системи числення (radix).

Зчитування символів з консолі з буферизацією здійснюють, розташувавши представника класу InputStreamReader в оболонку об’єкту класу BufferedReader. Загальна форма конструктора класу BufferedReader така:

BufferedReader(i);

Тут i — потік даних (файл, консоль тощо), тип Reader. У випадку консолі задають представника класу InputStreamReader. Такий підхід реалізує паттерн Декоратор (Decorator). У паттерні Декоратор екземпляр одного класу служить оболонкою для екземпляру іншого класу. Таким чином відбувається нашаровування об’єктів — див. приклад зчитування символів з консолі з буферизацією.

package work;
import java.io.*;
public class Work
{ public static void main(String[] args) throws IOException
  { PrintStream         o = System.out;
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader     br = new BufferedReader(isr);
    char c;
    o.println("Введіть символи, '/' - для виходу.");
    do { c = (char) br.read();
         o.println(c);
       }
    while (c != '/');
  }
}

з таким можливим виглядом консолі.

Введіть символи, '/' - для виходу.
qw
q
w


e/
e
/

Введення з консолі й розбиття рядків проілюструємо таким прикладом:

package work;
import java.io.*;
import java.util.Scanner;
public class Work
{ public static void main(String[] args) throws IOException
  { Scanner in = new Scanner(System.in);
    System.out.print("Введіть рядок тексту: слова, розділені пробілом.\n");
    String s = in.nextLine();
    String[] t = s.split(" "); // розбиття за роздільником " " - пробілом 
    System.out.println("Введений рядок містить "+t.length+" слів."); 

    for (int j=0;  j<t.length; j++) {System.out.println(t[j]);}
  }
}
Метод split класу String має два аргументи:

Клас File містить кілька конструкторів файлових об'єктів, кожен з яких дозволяє формувати назву файлу (у тому числі теки) різними способами. Найуживанішим є конструктор, що отримує один рядок типу String, уякому записано повну (абсолютну) або скорочену (відносну) назву файлу, який пов'язують з файловим об'єктом. Також є конструктори, що формують назву файлу з кількох частин — див. приклад з використанням методу getPath, описаного нижче.

package work;
import java.io.File;
public class Work
{ public static void main(String[] args)
  { File f1 = new File("output.txt");
    System.out.println(f1.getPath());

    File f2 = new File("/home", "/chief");
    System.out.println(f2.getPath());

    File f3 = new File(f2,"output.txt");
    System.out.println(f3.getPath());
  }
}

Результат виконання програми такий:

output.txt
/home/chief
/home/chief/output.txt

Основні методи класу File

Клас FileOutputStream призначено для запису байтів у файл. Він є похідним від класу OutputStream, тому успадковує всю його функціональність. Конструктор FileOutputStream має один два аргументи:

Якщо вказаного файлу немає, то його буде автоматично створено. Для записку рядка його потрібно спочатку перетворити у масив байтів — див. приклад запису рядка у файл.

package work;
import java.io. *;
public class Work
{ public static void main (String [] args)
  { String s = "Текст для запису"; // рядок для запису
    try (FileOutputStream fout = new FileOutputStream ("output.txt"))
      { byte [] b = s.getBytes();  // перетворення рядка у масив байтів
        fout.write (b, 0, b.length);
      }
    catch (IOException ex) {System.out.println(ex.getMessage());}
    System.out.println ("Файл успішно записано");
  }
}

Для автоматичного закриття файлу і звільнення ресурсу об'єкт FileOutputStream створюють за допомогою вказівки вигляду try … catch. При потребі записати лише один (перший) байт використовують перевантаження методу write:

fout.write(b[0]);

Клас FileInputStream призначено для зчитування даних з файлу. Він є спадкоємцем класу InputStream і реалізує всі його методи. Для створення об'єкта FileInputStream використовують декілька конструкторів. Найчастіше використовуваний як параметр використовує рядок s — шлях до зчитувати файлу:

FileInputStream (s) throws FileNotFoundException

Якщо файл не можливо відкрити (наприклад, за вказаним шляхом такого файлу немає), то буде породжено виключення FileNotFoundException.

Розглянемо приклад виведення у консоль даних з раніше створеного файлу.

package work;
import java.io. *;
public class Work
{ public static void main (String [] args)
  { try (FileInputStream fin = new FileInputStream ("input.txt"))
      { System.out.printf ( "Розмір файлу: %d байт \n",fin.available());
        int i = -1;
        while ( (i = fin.read())  != -1) {System.out.print ((char) i);}
      }
    catch (IOException ex) {System.out.println (ex.getMessage());}
  }
}

У розглянутому прикладі байти зчитють у змінну i доти, поки поток непорожній, тобто поки метод read число, відмінне від -1. Потім кожний зчитаний байт перетворюють в об'єкт типу char і виводять у консоль. Якщо текст містить літери кирилиці, можливе неправильне відображення вмісту файлу в консолі.

Перевірку умови fin.read()) != -1 можна замінити на перевірку справдження виразу fin.ready().

Переписати вміст одного файлу в інший можна таким чином.

package work;
import java.io. *;
public class Work
{ public static void main (String [] args)
  { try (FileInputStream  fin  = new FileInputStream ("input.txt");
         FileOutputStream fout = new FileOutputStream("output.txt")
        )
      { byte [] b = new byte [fin.available()];
        fin.read  (b, 0, b.length);
        fout.write(b, 0, b.length);
      }
    catch (IOException ex) {System.out.println(ex.getMessage());}
  }
}

Класи FileInputStream і FileOutputStream призначено для запису і читання байтів. Їх можна використовувати для роботи з текстовими файлами. Але для цього зручніше використовувати інші класи — нащадки класів Reader і Writer.

Клас FileReader є нащадком абстрактного класу Reader і надає функціональність для читання текстових файлів. Єдиний аргумент його конструктора може мати один з таких типів:

Клас FileWriter. Зазвичай використовують конструктор класу з такими двома аргументами:

Є що конструктор з одним аргументом типу FileDescriptor.

Якщо вказаного файлу немає, то його буде створено автоматично.

Наступний приклад ілюструє переписування вмісту файлу input.txt у файл output.txt

package work;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class Work
{ public static void main(String[] args)
  { try
    { FileReader in = new FileReader( "input.txt");
      FileWriter out= new FileWriter("output.txt");   
      BufferedReader bin = new BufferedReader(in);
      String s = bin.readLine();
      while (s != null)
      { out.write(s+"\n");
        System.out.println(s);
        s = bin.readLine();
      }
      out.close();
    }
    catch (FileNotFoundException e) {}
    catch (IOException e)           {}
  }
}

Текст упорядкував Олександр Рудик.