Пользователь Professor Hans Noodles
Professor Hans Noodles
41 уровень

Класс Scanner

Статья из группы Java Developer
Привет! Наше сегодняшнее занятие будет особенным! До этого при решении задач и написании программ алгоритм был простым: мы пишем какой-то код, запускаем метод main(), программа делает то, что от нее требуется, и завершает свою работу. Но теперь всё изменится! Сегодня мы научимся по-настоящему взаимодействовать с программой: мы научим её реагировать на наши действия! Возможно, ты уже понял, к чему мы клоним. Эту лекцию посвятим подробному разбору одного из классов языка Java – Scanner. Этот класс пригодится, если тебе нужно будет считывать данные, которые вводят юзеры. Перед тем, как мы перейдем к изучению кода, скажи, тебе когда-нибудь приходилось встречаться с таким устройством как сканер? Класс Scanner - 1Наверняка да. Изнутри строение сканера достаточно сложное, но суть его работы довольно проста: он считывает те данные, которые пользователь в него вводит (например, паспорт или страховой полис) и сохраняет считанную информацию в памяти (например, в виде изображения). Так вот, сегодня ты создашь свой собственный сканер! С документами он, конечно, не справится, а вот с текстовой информацией — вполне :) Поехали!

Java Scanner Class

Первое и главное, с чем нам нужно познакомиться, — класс java.util.Scanner. Его функциональность очень проста. Он, словно настоящий сканер, считывает данные из источника, который ты для него укажешь. Например, из строки, из файла, из консоли. Далее он распознает эту информацию и обрабатывает нужным образом. Приведем самый простой пример:

public class Main {

   public static void main(String[] args) {

       Scanner scanner = new Scanner("Люблю тебя, Петра творенье,\n" +
               "Люблю твой строгий, стройный вид,\n" +
               "Невы державное теченье,\n" +
               "Береговой ее гранит");
       String s = scanner.nextLine();
       System.out.println(s);
   }
}
Мы создали объект сканера и указали для него источник данных (строку с текстом). Метод nextLine() обращается к источнику данных (нашему тексту с четверостишием), находит там следующую строку, которую он еще не считывал (в нашем случае — первую) и возвращает ее. После чего мы выводим ее на консоль: Вывод в консоль:

Люблю тебя, Петра творенье,
Мы можем использовать метод nextLine() несколько раз и вывести весь кусок поэмы:

public class Main {

   public static void main(String[] args) {

       Scanner scanner = new Scanner("Люблю тебя, Петра творенье,\n" +
               "Люблю твой строгий, стройный вид,\n" +
               "Невы державное теченье,\n" +
               "Береговой ее гранит");
       String s = scanner.nextLine();
       System.out.println(s);
       s = scanner.nextLine();
       System.out.println(s);
       s = scanner.nextLine();
       System.out.println(s);
       s = scanner.nextLine();
       System.out.println(s);
   }
}
Каждый раз наш сканер будет делать один шаг вперед и считывать следующую строку. Результат работы программы — вывод в консоль:

Люблю тебя, Петра творенье,
Люблю твой строгий, стройный вид,
Невы державное теченье,
Береговой ее гранит
Как мы уже говорили, источником данных для сканера может быть не только строка, но и, например, консоль. Важная новость для нас: если раньше мы только выводили туда данные, теперь будем вводить данные с клавиатуры! Посмотрим, что еще умеет класс Scanner:

public class Main {

   public static void main(String[] args) {

       Scanner sc = new Scanner(System.in);
       System.out.println("Введите число:");

       int number = sc.nextInt();

       System.out.println("Спасибо! Вы ввели число " + number);

   }
}
Метод nextInt() считывает и возвращает введенное число. В нашей программе он используется для того, чтобы присвоить значение переменной number. Это уже больше похоже на настоящий сканер! Программа просит пользователя ввести в строку любое число. После того, как пользователь это сделал, программа благодарит его, выводит на консоль итог своей работы и завершается. Но у нас осталась одна серьезная проблема. Пользователь может ошибиться и ввести что-то не то. Вот пример, когда наша текущая программа перестанет работать:

public class Main {

   public static void main(String[] args) {

       Scanner sc = new Scanner(System.in);
       System.out.println("Введите число:");

       int number = sc.nextInt();

       System.out.println("Спасибо! Вы ввели число " + number);

   }
}
Попробуем ввести вместо числа строку “JavaRush”: Вывод в консоль:

Введите число:
JavaRush
Exception in thread "main" java.util.InputMismatchException
  at java.util.Scanner.throwFor(Scanner.java:864)
  at java.util.Scanner.next(Scanner.java:1485)
  at java.util.Scanner.nextInt(Scanner.java:2117)
  at java.util.Scanner.nextInt(Scanner.java:2076)
  at Main.main(Main.java:10)

Process finished with exit code 1
Ой-ой, все плохо -_- Во избежание подобных ситуаций нам нужно придумать способ проверки данных, которые вводит пользователь. Например, пользователь вводит что угодно, кроме числа, хорошо бы вывести в консоль предупреждение, что введенная информация не является числом, а если все в порядке — вывести текст подтверждения. Но для этого нам надо фактически “заглянуть в будущее” — узнать, что там дальше в нашем потоке. Умеет ли Scanner в Java это делать? Еще как умеет! И для этого у него есть целая группа методов: hasNextInt() — метод проверяет, является ли следующая порция введенных данных числом, или нет (возвращает, соответственно, true или false). hasNextLine() — проверяет, является ли следующая порция данных строкой. hasNextByte(), hasNextShort(), hasNextLong(), hasNextFloat(), hasNextDouble() — все эти методы делают то же для остальных типов данных. Попробуем изменить нашу программу для чтения числа:

public class Main {

   public static void main(String[] args) {

       Scanner sc = new Scanner(System.in);
       System.out.println("Введите число:");

       if (sc.hasNextInt()) {
           int number = sc.nextInt();
           System.out.println("Спасибо! Вы ввели число " + number);
       } else {
           System.out.println("Извините, но это явно не число. Перезапустите программу и попробуйте снова!");
       }

   }
}
Теперь наша программа проверяет, является ли следующий введенный символ числом или нет. И только в случае, если является, выводит подтверждение. Если же ввод не прошел проверку, программа это замечает и просит попробовать снова. По сути, ты можешь общаться с объектом Scanner и заранее узнавать, какой тип данных тебе ожидать. “Эй, сканер, что там дальше будет? Число, строка, или еще что? Число? А какое — int, short, long?” Такая гибкость дает тебе возможность выстраивать логику своей программы в зависимости от поведения пользователя. Еще один важный метод, на который стоит обратить внимание — useDelimiter(). В этот метод передается строка, которую вы хотите использовать в качестве разделителя. Класс Scanner - 2Например, мы неожиданно увлеклись японской поэзией и решили считать с помощью сканера несколько хокку великого поэта Мацуо Басе. Даже если три разных стиха переданы нам одной корявой строкой, мы легко можем их разделить и красиво отформатировать:

public class Main {
   public static void main(String[] args) {
       Scanner scan = new Scanner("На голой ветке'" +
               "Ворон сидит одиноко.'" +
               "Осенний вечер." +
               "''***''" +
               "В небе такая луна,'" +
               "Словно дерево спилено под корень:'" +
               "Белеет свежий срез." +
               "''***''" +
               "Как разлилась река!'" +
               "Цапля бредет на коротких ножках,'" +
               "По колено в воде.");

       scan.useDelimiter("'");

       while (scan.hasNext()) {
           System.out.println(scan.next());
       }

       scan.close();
   }
}
Мы используем в качестве разделителя шаблон "\n/*/*/*" (перенос строки и три звездочки). В результате в консоли у нас появится красивый вывод, совсем как в книгах:
На голой ветке
Ворон сидит одиноко.
Осенний вечер.

*** 
 
В небе такая луна,
Словно дерево спилено под корень:
Белеет свежий срез.

*** 
 
Как разлилась река!
Цапля бредет на коротких ножках,
По колено в воде.
В этом же примере есть еще один метод, на который нужно обязательно обратить внимание — close(). Как и любой объект, работающий с потоками ввода-вывода, сканер должен быть закрыт по завершении своей работы, чтобы больше не потреблять ресурсы нашего компьютера. Никогда не забывай о методе close()!

public class Main {

   public static void main(String[] args) {

       Scanner sc = new Scanner(System.in);
       System.out.println("Введите число:");

       int number = sc.nextInt();

       System.out.println("Спасибо! Вы ввели число " + number);
      
       sc.close();//вот теперь мы сделали все правильно!

   }
}
Вот и все! Как видишь, класс Scanner достаточно прост в использовании и очень полезен! :)
Комментарии (186)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
Margarita Zamanova 3 уровень, Санкт-Петербург
29 апреля 2021
Получается, когда пишешь \n" - то этот шаблон используется по умолчанию как делитель (перенос) строки, а если хочешь назначить свой делитель (другой значок), то используешь Delimeter
Margarita Zamanova 3 уровень, Санкт-Петербург
29 апреля 2021
Delimiter использует значок ' для переноса строки "Ворон сидит одиноко.' " здесь "\n/*/*/*" непонятно, проще написать как в примере " ' ' *** ' ' " - так ясно что один апостроф переносит строку на пустую, 2-ой с пустой на ***, и т.п. Незнаю, все ж понятно
Nagat 4 уровень, Ростов-на-Дону
2 апреля 2021
Как именно сработал Делиметр не понятно что такое \n тоже не понятно, этого не было нигде здесь. В итоге от статьи возникает больше вопросов чем ответов и приходится гуглить это все.
JoyStick 4 уровень, Тверь
31 марта 2021
ребят, как IntelliJ IDEA свою к джаве раш подрубить?
ihaweetqv 3 уровень, Bilky
19 марта 2021
почему в всех консолях код Scanner scanner = new Scanner(System.in); не работает? там выбивает ошибку из-за того что там 2 Сканера
Aragoz 6 уровень, Брест
21 февраля 2021
Добрый день Посмотрите мой код пож. import java.util.Scanner; public class test2 { public static void main(String[] args) { Scanner scan = new Scanner("На голой ветке," + "Ворон сидит одиноко," + "Осенний вечер,***," + "В небе такая луна," + "Словно дерево спилено под корень," + "Белеет свежий срез,***," + "Как разлилась река." + "Цапля бредет на коротких ножках," + "По колено в воде,"); scan.useDelimiter(","); while (scan.hasNext()) { System.out.println(scan.next()); } scan.close(); } } Результат: На голой ветке Ворон сидит одиноко Осенний вечер *** В небе такая луна Словно дерево спилено под корень Белеет свежий срез *** Как разлилась река.Цапля бредет на коротких ножках По колено в воде Так вот вопрос, а как учесть в scan.useDelimiter(",") не только запятую, но и точку одновременно?
Anonymous #2523187 4 уровень
4 февраля 2021
package com.company; import java.util.Scanner; class Scanner_test { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("Put in type,please"); String type = sc.nextLine(); System.out.println("Put here age,please"); if (sc.hasNextInt()) { int age = sc.nextInt(); System.out.println("YEEES"); } else{ System.out.println("noooo"); } Scanner nx = new Scanner(System.in); System.out.println("Breed"); String breed = nx.nextLine();
Anonymous #2288933 8 уровень
28 января 2021
Почему после sc.nextInt не срабатывает sc.nextLine?

import java.util.Scanner;

public class Scanner_test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Пожалуйста, ведите Ваше имя:");
        String name = sc.nextLine();
        System.out.println("Пожалуйста, ведите Ваш возраст:");
        int age = sc.nextInt();
        System.out.println("Пожалуйста, ведите Ваш пол:");
        String sex = sc.nextLine();




        System.out.println("Спасибо! Вы указали имя: " + name +
                "\n         Ваш возраст: " + age +
                "\n         Ваш пол: " + sex);

    }
}
Irina Alymova 35 уровень, Киев
28 января 2021
https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html Сканнер обычно использует пробел как разделитель, но можно это изменить. Разделитель - это то, что в тексте сканнер должен игнорировать. String input = "1 fish 2 fish red fish blue fish"; Scanner s = new Scanner(input).useDelimiter("fish"); System.out.println(s.nextInt()); System.out.println(s.nextInt()); System.out.println(s.next()); System.out.println(s.next()); s.close(); Получаем: 1 2 red blue В статье просто ошибка, по данной логике звездочки должны были исчезнуть при выводе текста на экран.
Стас 14 уровень, Москва
25 января 2021
Тот же вопрос. Где ответ - видимо далеко дальше или в книгах?! или он как-то особо работает... По текущему примеру - метод scan.useDelimiter() визуально ничего с выводимым текстом не делает. Без него все тоже самое. escape-последовательность \n благополучно работает и в конце каждой 3й строки делает 2 переноса на новые строки, после чего идут ***.