Вступление
Чтение пользовательского ввода - это первый шаг к написанию полезного программного обеспечения Java. Пользовательский ввод может иметь разные формы - взаимодействие с мышью и клавиатурой, сетевой запрос, аргументы командной строки, файлы, которые обновляются данными, имеющими отношение к выполнению программы, и т. Д.
Мы собираемся сосредоточиться на вводе с клавиатуры через так называемый
стандартный поток ввода . Вы можете узнать его как Java's
System.in
.
Мы собираемся использовать Scanner
чтобы упростить наше взаимодействие
с базовым потоком. Поскольку у Scanner
есть некоторые недостатки, мы
также будем использовать BufferedReader
и InputStreamReader
для
обработки потока System.in
В конце концов, мы будем Украсьте InputStream
класс и реализовать
свой собственный пользовательский UncloseableInputStream
для решения
вопросов с Scanner
класса.
Класс Java-сканера
Класс java.util.Scanner
- это простой сканер, который может
анализировать и обрабатывать примитивные входные данные, строки и
потоки. Поскольку System.in
- это просто InputStream
, мы можем
создать Scanner
как таковой:
Scanner sc = new Scanner(System.in);
Этот Scanner
теперь может сканировать и анализировать логические,
целые, числа с плавающей запятой, байты и строки.
Давайте посмотрим, как мы можем извлечь информацию из Scanner
в
переменные, с которыми мы можем работать:
Scanner sc = new Scanner(System.in);
// Read an integer into a variable
int myInteger = sc.nextInt();
// Read a byte into a variable
byte myByte = sc.nextByte();
// Read a line until newline or EOF into a string
String myLine = sc.nextLine();
// Closing the scanner
sc.close();
Опять же, конструктор не должен использовать System.in
. Он может
принимать любой File
, InputStream
, Readable
,
ReadableByteChannel
, Path
(файла для чтения) или даже String
.
Кроме того, в качестве второго аргумента он может указать кодировку
символов для интерпретации указанных символов:
Scanner sc = new Scanner(new FileInputStream("myFile.txt"), "UTF-8");
Обратите внимание, что Сканер необходимо закрыть, когда вы закончите с ним работать. Самый простой способ сделать это - использовать оператор try-with-resources.
Способы чтения с помощью сканера
Доступные методы чтения следующего токена с помощью метода сканирования:
Методика Тип возврата Описание следующий() Нить Находит и возвращает следующий полный токен со сканера. nextByte () байт Сканирует следующий токен ввода как байт. nextDouble () двойной Сканирует следующий токен ввода как двойной. nextFloat () плавать Сканирует следующий токен ввода как число с плавающей запятой. nextInt () int Сканирует следующий токен ввода как int. nextLong () длинный Сканирует следующий токен ввода как длинный. nextShort () короткая Сканирует следующий токен ввода как короткий. nextBoolean () логический Сканирует следующий токен ввода в логическое значение и возвращает это значение. nextLine () Нить Перемещает этот сканер за пределы текущей строки и возвращает пропущенный ввод.
Стоит отметить метод hasNext()
- общий метод, который вернет true
если существует какой-либо тип токена для чтения. Существуют методы,
зависящие от типа, такие как hasNextInt()
, hasNextFloat()
,
hasNextLine()
и т. Д., Которые вы можете использовать таким же
образом.
Проблемы при использовании System.in со сканером
Большая проблема с System.in
том, что это InputStream
. При работе с
ним Scanner
всегда ожидает большего ввода, пока InputStream
будет
закрыт. Как только поток будет закрыт, мы больше не сможем получить
доступ к вводу со Scanner
.
Помимо закрытия самого себя, Scanner
также закроет InputStream
если
он реализует Closeable
.
Поскольку InputStream
делает это, это означает, что Scanner
закроет
поток System.in
для всей вашей программы .
При этом, если вы закроете Scanner
и, следовательно, System.in
, вы
не сможете снова System.in
Scanner sc = new Scanner(System.in);
System.out.println(sc.nextInt());
sc.close();
System.out.println("Closing the scanner...");
sc = new Scanner(System.in);
System.out.println(sc.nextInt());
sc.close();
System.out.println("Closing the scanner...");
Это приводит к:
1
1
Closing the scanner...
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:862)
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 com.company.Main.main(Main.java:18)
Это значительно усложняет работу со Scanner
и System.in
. Мы
исправим это в последнем разделе.
BufferedReader и InputStreamReader
Вместо Scanner
вы также можете использовать BufferedReader
вместе с
InputStreamReader
для получения пользовательского ввода:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while((line = br.readLine()) != null){
System.out.println(String.format("The input is: %s", line));
}
Здесь мы просто повторяем входную строку с префиксом:
Hello!
The input is: Hello!
I'd like to order some extra large fries.
The input is: I'd like to order some extra large fries.
^D
Process finished with exit code 0
BufferedReader
хорошо подходит для чтения строк, но не имеет
встроенных методов для обработки чисел. Чтобы прочитать целое число, вам
нужно будет проанализировать его из String:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int a = Integer.parseInt(br.readLine());
System.out.println(a);
Теперь это отлично работает:
5
5
Пользовательский Uncloseable InputStream
К счастью, есть решение, как Scanner
закрывает поток System.in
благодаря шаблону проектирования
Decorator
. Мы можем реализовать наш собственный InputStream
и просто заставить
метод close()
ничего не делать, чтобы при его Scanner
не влиял на
базовый стандартный ввод:
public class UnclosableInputStreamDecorator extends InputStream {
private final InputStream inputStream;
public UnclosableInputStreamDecorator(InputStream inputStream) {
this.inputStream = inputStream;
}
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public int read(byte[] b) throws IOException {
return inputStream.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return inputStream.skip(n);
}
@Override
public int available() throws IOException {
return inputStream.available();
}
@Override
public synchronized void mark(int readlimit) {
inputStream.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
inputStream.reset();
}
@Override
public boolean markSupported() {
return inputStream.markSupported();
}
@Override
public void close() throws IOException {
// Do nothing
}
}
Когда мы модифицируем наш проблемный код для использования
настраиваемого InputStream
, он будет выполняться без проблем:
public class ScannerDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(new UnclosableInputStreamDecorator(System.in));
System.out.println(sc.nextInt());
sc.close();
System.out.println("Closing the scanner...");
sc = new Scanner(new UnclosableInputStreamDecorator(System.in));
System.out.println(sc.nextInt());
sc.close();
System.out.println("Closing the scanner...");
}
}
Выполнение этого приведет к:
1
1
Closing the scanner...
1
1
Closing the scanner...
Заключение
В этой статье мы рассмотрели, как использовать Scanner
для чтения
пользовательского ввода. Затем мы использовали BufferedReader
вместе с
InputStreamReader
в качестве альтернативного подхода.
Наконец, мы реализовали наш собственный InputStream
чтобы избежать
того, что Scanner
закроет System.in
для всей программы.
Надеюсь, вы узнали, как обрабатывать базовый ввод в консоль в Java, и узнали некоторые распространенные ошибки, с которыми вы можете столкнуться в процессе.