Scanner.next: примеры (JAVA)
Scanner.next: StringОписание метода Scanner.next
Метод next() класса java.util.Scanner возвращает следующий токен входного потока в виде строки. Токен определяется текущим разделителем (по умолчанию это пробельные символы). Метод считывает до следующего разделителя и возвращает найденную подстроку.
Когда используется: при поэтапном разборе потоков текста, когда требуется получить отдельные слова, числа в виде строк или куски данных, разделённые заданным шаблоном.
Аргументы и перегрузки: сам next() не принимает аргументов. В API Scanner доступны родственные методы, которые принимают шаблоны:
- next(Pattern pattern) - возвращает следующий токен, соответствующий переданному регулярному выражению.
- next(String pattern) - аналогично принимает строковый вид шаблона и ищет следующий токен по этому шаблону.
Возвращаемое значение: String - текст следующего найденного токена без разделителей.
Поведение и исключения:
- Если токенов нет - выбрасывается NoSuchElementException.
- Если сканер закрыт - выбрасывается IllegalStateException.
- Для разбора чисел существуют отдельные методы (nextInt, nextDouble и т. п.) - при их использовании возможна InputMismatchException, если токен не соответствует ожидаемому числовому формату.
Особенности: разбиение на токены регулируется через метод useDelimiter(Pattern) или useDelimiter(String). Метод next() читает только до разделителя и не возвращает сам разделитель; в отличие от nextLine(), который возвращает остаток строки до символа новой строки.
Короткие примеры использования
Примеры показывают поведение метода next() с разными входными данными и шаблонами.
1) Базовое чтение токенов из строки:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner("Hello World 123");
System.out.println(sc.next());
System.out.println(sc.next());
System.out.println(sc.next());
sc.close();
}
}
Hello World 123
2) Различие next() и nextLine():
import java.util.Scanner;
class A {
public static void main(String[] args) {
Scanner sc = new Scanner("First line\nSecond line");
System.out.println("next(): '" + sc.next() + "'");
System.out.println("nextLine(): '" + sc.nextLine() + "'");
sc.close();
}
}
next(): 'First' nextLine(): ' line'
3) Использование шаблона с next(Pattern):
import java.util.Scanner;
import java.util.regex.Pattern;
class B {
public static void main(String[] args) {
Scanner sc = new Scanner("abc123xyz 456");
System.out.println(sc.next(Pattern.compile("[a-z]+[0-9]+")));
sc.close();
}
}
abc123
Альтернативы в Java и особенности
Несколько альтернатив для ввода и разбора данных в Java:
- BufferedReader.readLine() - считывает строчки целиком. Предпочтительнее для построчной обработки больших объёмов и при необходимости высокой производительности. Возвращает строку или null при окончании потока.
- Console.readLine() - удобен для интерактивного ввода в консоли (ищет системную консоль). Не всегда доступен (например, в IDE).
- StringTokenizer - устаревший утилитарный класс для разбиения строк; проще, но менее гибкий и не рекомендуется для новых решений.
- StreamTokenizer - более низкоуровневый разбор, даёт контроль над лексемами, полезен при парсинге с нестандартной грамматикой.
Выбор: если нужен простой разбор на слова - Scanner удобен; для скорости и обработки больших файлов - BufferedReader; для ввода паролей или безопасного ввода - Console; для сложного лексического анализа - StreamTokenizer или собственный парсер.
Аналоги в других языках
Краткие примеры эквивалентов метода next() в других языках и их отличия.
PHP - чтение токенов из строки с strtok или explode:
$s = "Hello World 123";
$tok = strtok($s, " ");
while ($tok !== false) {
echo $tok . PHP_EOL;
$tok = strtok(" ");
}
Hello World 123
JavaScript (Node.js) - разбить строку по пробелам:
const s = 'Hello World 123';
const tokens = s.split(/\s+/);
console.log(tokens.join('\n'));
Hello World 123
Python - split или итерация по sys.stdin:
s = 'Hello World 123'
for tok in s.split():
print(tok)
Hello World 123
C# - Scanner-аналоги: String.Split или TextReader: простой пример:
using System;
class P { static void Main(){
string s = "Hello World 123";
foreach(var t in s.Split((char[])null, StringSplitOptions.RemoveEmptyEntries))
Console.WriteLine(t);
}}
Hello World 123
Go - fmt.Fscan / bufio.Scanner:
package main
import (
"bufio"
"fmt"
"strings"
)
func main(){
s := "Hello World 123"
sc := bufio.NewScanner(strings.NewReader(s))
sc.Split(bufio.ScanWords)
for sc.Scan() {
fmt.Println(sc.Text())
}
}
Hello World 123
Kotlin - использует тот же Scanner или split:
fun main(){
val s = "Hello World 123"
s.split(Regex("\\s+")).forEach { println(it) }
}
Hello World 123
Lua - использование ipairs по результату split (через внешний код):
local s = "Hello World 123"
for w in s:gmatch("%S+") do
print(w)
end
Hello World 123
Особенности: во многих языках работа с токенами делегируется методам строки (split, gmatch) или специализированным сканерам; отличие от Java: в Java Scanner объединяет чтение из разных источников и разбор токенов в одном классе.
Типичные ошибки при использовании
Основные ошибки и примеры.
1) Попытка читать, когда токенов нет - NoSuchElementException:
import java.util.Scanner;
class E1 {
public static void main(String[] args) {
Scanner sc = new Scanner("");
System.out.println(sc.next());
}
}
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Scanner.throwFor(Scanner.java:937)
at java.base/java.util.Scanner.next(Scanner.java:1594)
...
Рекомендация: перед вызовом next() проверять hasNext().
2) Использование next() после nextInt()/nextDouble() без обработки остатка строки:
import java.util.Scanner;
class E2 {
public static void main(String[] args) {
Scanner sc = new Scanner("10\nrest");
int n = sc.nextInt();
String s = sc.nextLine();
System.out.println("n=" + n);
System.out.println("s='" + s + "'");
}
}
n=10 s=''
Пояснение: nextInt() не потребляет символ новой строки, поэтому следующий вызов nextLine() вернёт пустую строку. Решение - добавить sc.nextLine() для пропуска остатка.
3) Закрытие сканера, читающего System.in, приводит к закрытию потока System.in и последующим ошибкам при попытке чтения:
Scanner sc = new Scanner(System.in);
sc.close();
// далее попытки чтения из System.in вызовут ошибки
(последующие операции ввода могут выбросить IllegalStateException или вести к невозможности чтения)
4) Ошибки при использовании nextInt вместо next при некорректном формате - InputMismatchException:
Scanner sc = new Scanner("abc");
sc.nextInt();
Exception in thread "main" java.util.InputMismatchException
at java.base/java.util.Scanner.throwFor(Scanner.java:937)
...Изменения и эволюция
Метод next() как поведение токенизации оставался стабильным в новых релизах Java. Основные изменения в классе Scanner за последние версии касались конструкторов, поддержки указания Charset при создании сканера, а также исправлений производительности и багов в разборе Unicode и регулярных выражений. Никаких радикальных изменений в семантике next() не происходило.
Расширенные и необычные примеры
1) Пользовательский разделитель (например, запятая или точка с запятой):
import java.util.Scanner;
class Adv1 {
public static void main(String[] args) {
Scanner sc = new Scanner("a,b;c,d");
sc.useDelimiter("[;,]");
while (sc.hasNext()) System.out.println(sc.next());
sc.close();
}
}
a b c d
2) Парсинг чисел с учетом локали (десятичный разделитель):
import java.util.Locale;
import java.util.Scanner;
class Adv2 {
public static void main(String[] args) {
Scanner sc = new Scanner("3,14 2.71");
sc.useLocale(Locale.FRANCE); // ожидает ',' для десятичной части
System.out.println(sc.nextDouble());
sc.useLocale(Locale.US);
System.out.println(sc.nextDouble());
sc.close();
}
}
3.14 2.71
3) Поиск токена, соответствующего регулярному выражению, с помощью next(Pattern):
import java.util.Scanner;
import java.util.regex.Pattern;
class Adv3 {
public static void main(String[] args) {
Scanner sc = new Scanner("id:42 name:john id:100");
Pattern idPat = Pattern.compile("id:\\d+");
while (sc.hasNext()) {
if (sc.hasNext(idPat)) {
System.out.println(sc.next(idPat));
} else {
sc.next(); // пропустить токен
}
}
sc.close();
}
}
id:42 id:100
4) Чтение больших файлов: сочетание BufferedReader и Scanner - антишаблон. Пример замедления при использовании Scanner для больших данных и альтернатива на BufferedReader:
// Антипаттерн: миллионы вызовов next() могут быть медленными
Scanner sc = new Scanner(new java.io.File("big.txt"));
while (sc.hasNext()) {
String tok = sc.next();
}
// Лучше: BufferedReader + ручная обработка строк
(практика показывает значительную разницу в скорости и потреблении памяти)
5) Комбинация next(Pattern) для сложного извлечения данных (пример извлечения пар key=value, где value может содержать пробелы, ограниченные кавычками):
import java.util.Scanner;
import java.util.regex.Pattern;
class Adv5 {
public static void main(String[] args) {
String s = "k1=val k2=\"multi word\" k3=123";
Scanner sc = new Scanner(s);
Pattern pair = Pattern.compile("\\w+=\\\"[^\\\"]+\\\"|\\w+=[^\\s]+");
while (sc.hasNext()) {
if (sc.hasNext(pair)) System.out.println(sc.next(pair));
else sc.next();
}
sc.close();
}
}
k1=val k2="multi word" k3=123
6) Параллельное использование нескольких Scanner на одном InputStream - рискованно: внутренние буферы могут конфликтовать. Для многопоточной работы следует использовать синхронизацию и отдельные независимые потоки/ридеры.