JavaRush /Java блог /Random UA /Зчитування з клавіатури – «рідери»

Зчитування з клавіатури – «рідери»

Стаття з групи Random UA
Вітання! У лекціях та завданнях ми навчабося виводити дані в консоль, і навпаки – зчитувати дані з клавіатури. Зчитування з клавіатури - «рідери» - 1Ти навіть навчився використовувати для цього складну конструкцію:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Але на одне запитання ми так і не відповіли.

А як це взагалі працює?

Насправді будь-яка програма найчастіше існує не сама по собі. Вона може спілкуватися з іншими програмами, системами, інтернетом та ін. Під словом "спілкуватися" ми насамперед подразнюємося "обмінюватися даними". Тобто приймати якісь дані ззовні, а власні дані навпаки, кудись відправляти. Прикладів обміну даними між програмами багато навіть у повсякденному житті. Так, на багатьох сайтах ти можеш замість реєстрації авторизуватися за допомогою свого облікового запису в Facebook або Twitter. У цій ситуації дві програми, скажімо, Twitter та сайт, на якому ти намагаєшся зареєструватися, обмінюються необхідними даними між собою, після чого ти бачиш кінцевий результат – успішну авторизацію. Для опису процесу обміну даними у програмуванні часто використовується термін “ потік”. Звідки взагалі взялася така назва? Потік більше асоціюється з річкою або струмком, ніж з програмуванням. Насправді, це недарма :) Потік - це, по суті, шматок даних, що переміщається. Тобто в програмуванні потоком “тече” не вода, а дані у вигляді байтів і символів. З потоку даних ми можемо отримувати дані частинами і щось із нею робити. Знову ж таки, застосуємо "водно-плинну" аналогію: з річки можна зачерпнути води, щоб зварити суп, згасити пожежу або полити квіти. За допомогою потоків ти можеш працювати з будь-якими джерелами даних: інтернет, файлова система твого комп'ютера або ще щось — без різниці. Потоки – інструмент універсальний. Вони дозволяють програмі отримувати дані звідусіль (вхідні потоки) і відправляти їх будь-куди (вихідні). Їхнє завдання одне - брати дані в одному місці і відправляти в інше. Потоки поділяються на два види:
  1. Вхідний потік ( Input ) - використовується для прийому даних
  2. Вихідний потік ( Output ) - для надсилання даних.
Вхідний потік даних Java реалізований в класі InputStream, вихідний - в класі OutputStream. Але є й інший спосіб поділу потоків. Вони діляться як на вхідні і вихідні, але й байтові і символьні . Тут сенс зрозумілий і пояснень: байтовий потік передає інформацію як набору байт, а символьний — як набору символів. У цій лекції ми докладно зупинимося на вхідних потоках. А інформацію про вихідні я докладу посилання наприкінці, і ти зможеш прочитати про це сам:) Отже, наш код:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Ти, мабуть, ще під час читання лекцій подумав, що це виглядає досить жахливо? :) Але це тільки доти, доки ми не розібралися, як ця штука працює. Зараз виправимо! Почнемо з кінця. System.in- Це об'єкт класу InputStream, про який ми говорабо на початку. Це вхідний потік, і він прив'язаний до системного введення даних - клавіатурі. Ви з ним, до речі, опосередковано знайомі. Адже ти часто використовуєш у роботі його "колегу" - System.out! System.out— це системний потік виведення даних, він використовується для виведення на консоль у тому самому методі System.out.println(), яким ти постійно користуєшся:) System.out— потік для надсилання даних на консоль, аSystem.in— щоб отримати дані з клавіатури. Все просто:) Більше того: щоб рахувати дані з клавіатури, ми можемо обійтися без цієї великої конструкції та написати просто: System.in.read();
public class Main {

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

       while (true) {
           int x = System.in.read();
           System.out.println(x);
       }
   }
}
У класі InputStreamSystem.in, нагадаю, є об'єктом класу InputStream) є метод read(), який дозволяє зчитувати дані. Одна проблема: він зчитує байти , а не символи . Спробуємо рахувати з клавіатури російську букву "Я". Виведення в консоль:
Я
208
175
10
Російські літери займають у пам'яті комп'ютера 2 байти (на відміну від англійських, які займають лише 1). В даному випадку з потоку вважалося 3 байти: два перші позначають нашу літеру "Я", і ще один - перенесення рядка (Enter). Тому варіант використовувати голий System.inнам не підійде. Людина (за рідкісними винятками!) не вміє читати байти. Тут нам на допомогу і приходить наступний клас - InputStreamReader! Давай розберемося, що то за звір такий.
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
Ми передаємо потік System.inоб'єкту InputStreamReader. Загалом, якщо перевести його назву російською, все виглядає очевидно - "зчитувач вхідних потоків". Власне, саме для цього він і потрібний! Ми створюємо об'єкт класу InputStreamReaderта передаємо йому вхідний потік, з якого він повинен зчитувати дані. В даному випадку...
new InputStreamReader(System.in)
...ми говоримо йому: “ти зчитуватимеш дані із системного вхідного потоку (з клавіатури)”. Але це не єдина його функція! InputStreamReaderяк отримує дані з потоку. Він ще й перетворює байтові потоки на символьні . Іншими словами, тобі вже не потрібно самому дбати про переклад лічених даних з “комп'ютерної” мови на “людську” – InputStreamReaderзробить усе за тебе. InputStreamReader, звичайно, може читати дані не лише з консолі, а й з інших місць. Наприклад, із файлу:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

   public static void main(String[] args) throws IOException {
       InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\username\\Desktop\\testFile.txt"));
   }
}
Тут ми створабо вхідний потік даних FileInputStream(це один з різновидів InputStream), передали в нього шлях до файлу, а сам потік передали InputStreamReader. Тепер він зможе читати дані з цього файлу, якщо файл цим шляхом існує, звичайно. Для читання даних (неважливо звідки, з консолі, файлу або ще звідкись) у класі InputStreamReaderтеж використовується метод read(). У чому різниця між System.in.read()і InputStreamReader.read()? Давай спробуємо рахувати ту саму букву "Я" за допомогою InputStreamReader. Нагадаю, ось що вважав System.in.read():
Я
208
175
10
А як ту саму роботу проробить 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);
       }
   }
}
Виведення в консоль:
Я
1071
10
Різницю видно відразу. Останній байт - для перенесення рядка - залишився без змін (число 10), а ось лічену букву "Я" було перетворено на єдиний код "1071". Це і є зчитування за символами! Якщо раптом не віриш, що код 1071 позначає букву "Я" - в цьому легко переконатися:)
import java.io.IOException;

public class Main {

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

       char x = 1071;
       System.out.println(x);
   }
}
Виведення в консоль:

Я
Але якщо InputStreamReaderтакий гарний, навіщо потрібен ще BufferedReader? InputStreamReaderвміє і зчитувати дані, і конвертувати байти в символи — а що нам ще треба? Для чого ще один Reader? :/ Відповідь дуже проста - для більшої продуктивності і більшої зручності . Почнемо з продуктивності. BufferedReaderпри зчитуванні даних використовує спеціальну область - буфер, куди "складає" прочитані символи. У результаті, коли ці символи знадобляться нам у програмі, вони будуть взяті з буфера, а не безпосередньо з джерела даних (клавіатури, файлу тощо), а це заощаджує дуже багато ресурсів. Щоб зрозуміти як це працює — уяви, наприклад, роботу кур'єра у великій компанії. Кур'єр сидить в офісі та чекає, коли йому принесуть посилки на доставку. Щоразу, отримавши нову посилку, він може відразу вирушати в дорогу. Але посилок протягом дня може бути багато, і йому доведеться щоразу мотатися між офісом та адресаами. Натомість кур'єр поставив в офісі коробку, куди всі бажаючі складають свої посилки. Тепер кур'єр може спокійно взяти коробку і вирушати за адресаами - він заощадить дуже багато часу. адже йому не доведеться щоразу повертатися до офісу. Коробка в цьому прикладі є буфером, а офіс — джерелом даних. Кур'єру набагато простіше при доставці брати лист із загальної коробки, ніж щоразу їхати до офісу. Ще й бензин заощадить. Так само і в програмі — набагато менш витратно за ресурсами брати дані з буфера, а не звертатися щоразу до джерела даних. ТомуBufferedReader+ InputStreamReaderпрацює швидше, ніж просто InputStreamReader. З продуктивністю розібралися, а що із зручністю? Головний плюс у тому, що BufferedReaderвміє читати дані не тільки по одному символу (хоча метод read()для цих цілей у нього теж є), а ще цілими рядками! Робиться це за допомогою методу 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);
   }
}
Виведення в консоль:
JavaRush - найкращий сайт для вивчення Java!
Мы считали с клавиатуры эту строку:
JavaRush — лучший сайт для изучения Java!
Це особливо зручно у разі читання великого обсягу даних. Одну-два рядки тексту можна вважати посимвольно. А ось вважати "Війну і мир" за однією літерою буде вже дещо проблематично :) Тепер робота потоків стала набагато зрозумілішою для тебе. Для подальшого вивчення – ось тобі додаткове джерело: Тут ти можеш прочитати докладніше про вхідні та вихідні потоки. Відеоогляд BufferedReader'a одного з наших учнів. Так-так, наші учні не лише навчаються самі, а й записують навчальні відео для інших! Не забудь поставити лайк і передплатити наш канал :)
Краще від початку навчання привчати себе до читання офіційної документації. Вона є основним джерелом знань з мови, і більшість відповідей завжди можна знайти там.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ