JavaRush /Java блог /Java Developer /Практика роботи з класами BuffreredReader і InputStreamRe...
Professor Hans Noodles
41 рівень

Практика роботи з класами BuffreredReader і InputStreamReader

Стаття з групи Java Developer
Привіт! Сьогоднішня лекція буде розділена на дві умовні частини. Ми повторимо дещо зі старих тем, яких уже торкалися раніше, і розглянемо деякі нові фічі :) Практика роботи з класами BuffreredReader і InputStreamReader - 1Почнемо з першого. Повторення – мати навчання :) Ти вже не раз користувався таким класом як BufferedReader. Цю команду, сподіваюся, ти ще не встиг забути:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Перш ніж читати далі, спробуй згадати за що відповідає кожна складова(System.in, InputStreamReader, BufferedReader) і для чого вони потрібні. Вийшло? Якщо ні – не страшно :) Коротенько згадаємо, що вміє кожен із них. System.in – це потік для отримання даних з клавіатури. У принципі, щоб реалізувати логіку читання тексту, нам вистачило б і його одного. Але, як ти пам'ятаєш, System.in вміє зчитувати тільки байти, а не символи:

public class Main {

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

       while (true) {
           int x = System.in.read();
           System.out.println(x);
       }
   }
}
Якщо ми виконаємо цей код і введемо в консолі букву "Й", виведення буде таким:
Й 208 153 10
Символи кирилиці займають у пам'яті 2 байти, які й виводяться на екран (а число 10 – це байтове представлення перенесення рядка, тобто натискання Enter). Читання байтів – таке собі задоволення, тому використовувати System.in у чистому вигляді буде незручно. Для того, щоб зчитувати зрозумілі всім кириличні (і не тільки) літери, ми використовуємо InputStreamReader як обгортку:

public class Main {

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

       InputStreamReader reader = new InputStreamReader(System.in);
       while (true) {
           int x = reader.read();
           System.out.println(x);
       }
   }
}
Якщо ми введемо в консоль ту саму букву "Й", результат цього разу буде іншим:
Й 1049 10
InputStreamReader привів два зчитані байти (208, 153) до єдиного числа 1049. Це і є зчитування за символами. 1049 відповідає букві "Й", у чому можна легко переконатися:

public class Main {

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

       char x = 1049;
       System.out.println(x);
   }
}
Виведення в консоль:
Й
Ну а що стосується BufferedReader'a (та й узагалі – BufferedЧогоЗавгодно), буферизовані класи використовують для оптимізації продуктивності. Звернення до джерела даних (файлу, консолі, ресурсу в Мережі) – досить дорога в плані продуктивності операція. Тому щоб скоротити кількість таких звернень, BufferedReader зчитує і накопичує дані в спеціальному буфері, звідки потім ми можемо їх отримати. Внаслідок цього кількість звернень до джерела даних скорочується в рази або навіть десятки разів! Ще одна додаткова фіча BufferedReader'a і його перевага над звичайним InputStreamReader'ом – це вкрай корисний метод readLine(), який зчитує дані цілими рядками, а не окремими числами. Це, звісно, сильно додає зручності під час реалізації, наприклад, великого тексту. Ось який вигляд матиме зчитування рядка:

public class Main {

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

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
       String s = reader.readLine();
       System.out.println("Користувач ввів такий текст:");
       System.out.println(s);
       reader.close();
   }
}
BufferedReader+InputStreamReader працює швидше, ніж просто InputStreamReader Користувач ввів такий текст: BufferedReader+InputStreamReader працює швидше, ніж просто InputStreamReader
Практика роботи з класами BuffreredReader і InputStreamReader - 2Зрозуміло, BufferedReader – дуже гнучкий механізм, і дає змогу працювати не тільки з клавіатурою. Зчитувати дані можна, наприклад, безпосередньо з Мережі, просто передавши рідеру потрібний URL:

public class URLReader {
   public static void main(String[] args) throws Exception {

       URL oracle = new URL("https://www.oracle.com/index.html");
       BufferedReader in = new BufferedReader(
               new InputStreamReader(oracle.openStream()));

       String inputLine;
       while ((inputLine = in.readLine()) != null)
           System.out.println(inputLine);
       in.close();
   }
}
Можна зчитувати дані з файлу, передавши шлях до нього:

public class Main {
   public static void main(String[] args) throws Exception {

       FileInputStream fileInputStream = new FileInputStream("testFile.txt");
       BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));

       Рядок str;

       while ((str = reader.readLine()) != null) {
           System.out.println (str);
       }

       reader.close();
   }
}

Підміна System.out

Тепер давай розглянемо одну цікаву можливість, якої ми раніше не торкалися. Як ти напевно пам'ятаєш, у класі System є два статичні поля – System.in та System.out. Ці брати-близнюки є об'єктами класів-потоків. System.in – абстрактного класу InputStream. А System.out – класу PrintStream. Зараз ми поговоримо саме про System.out. Якщо ми зайдемо у вихідний код класу System, побачимо ось що:

public final class System {

……………...

public final static PrintStream out = null;

  …………

}
Отже, System.out – просто звичайна статична змінна класу System. Ніякої магії в ній немає :) Змінна out належить до класу PrintStream. Ось цікаве запитання: а чому під час виконання коду System.out.println() виведення здійснюється саме в консоль, а не кудись ще? І чи можна це якось змінити? Наприклад, ми хочемо зчитувати дані з консолі і записувати їх у текстовий файл. Чи можна якось реалізувати таку логіку, не використовуючи додаткові класи рідерів і райтерів, а просто користуючись System.out? Ще й як можна :) І хоча змінна System.out позначена модифікатором final, ми все одно можемо це зробити! Практика роботи з класами BuffreredReader і InputStreamReader - 3Отже, що нам для цього потрібно? По-перше, потрібен новий об'єкт класу PrintStream замість наявного. Поточний об'єкт, встановлений у класі System за замовчуванням, нам не підходить: він вказує на консоль. Треба створити новий, який вказуватиме на текстовий файл як "місце призначення" для наших даних. По-друге, потрібно зрозуміти, як присвоїти нове значення змінній System.out. Просто так цього не зробити, адже вона позначена final. Почнемо з кінця. У класі System якраз є потрібний нам метод – setOut(). Він приймає на вхід об'єкт PrintStream і встановлює його як точку виведення. Якраз те, що нам потрібно! Залишилося тільки створити об'єкт PrintStream. Це зробити теж нескладно:

PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
Код цілком матиме такий вигляд:

public class SystemRedirectService {

   public static void main(String arr[]) throws FileNotFoundException
   {
       PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));

       /*Збережемо поточне значення System.out в окрему змінну, щоб потім
       можна було переключитися назад на виведення в консоль*/.
       PrintStream console = System.out;

       // Присвоюємо System.out нове значення
       System.setOut(filePrintStream);
       System.out.println("Цей рядок буде записано в текстовий файл");

       // Повертаємо System.out старе значення
       System.setOut(console);
       System.out.println("А цей рядок – у консоль!");
   }
}
Як наслідок, перший рядок буде записано в текстовий файл, а другий – виведено в консоль :) Ти можеш скопіювати цей код у своє IDE і запустити. Відкривши текстовий файл, ти побачиш, що потрібний рядок успішно туди записався :) На цьому лекція добігає кінця. Сьогодні ми пригадали, як працювати з потоками і рідерами, відновили в пам'яті, чим вони відрізняються один від одного і дізналися про нові можливості System.out, якою користувалися мало не в кожному уроці :) До зустрічі на наступних лекціях!
Коментарі (1)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Юлія Рівень 48 Expert
9 липня 2023
Чудова лекція.Приклад з PrintStream дуже цікавий!