Java工具类Scanner详解:用户输入与文本解析的全能指南
Scanner是Java中一个极其实用的工具类,它能够将输入文本分解为基本类型和字符串,大大简化了用户输入和文本解析的过程。本文将全面深入地剖析Scanner类的各个方面,从基础使用到高级技巧,帮助你掌握这个强大的输入解析工具。
一、Scanner概述
1.1 Scanner是什么?
Scanner是java.util
包中的一个类,用于解析基本类型和字符串的简单文本扫描器。它可以将输入文本(如文件、字符串或标准输入流)分解为标记(tokens),然后使用各种next
方法将其转换为不同类型的值。
1.2 Scanner的核心功能
- 从多种输入源读取数据(控制台、文件、字符串等)
- 将输入分解为标记(默认以空白符分隔)
- 将标记转换为各种基本数据类型
- 支持正则表达式匹配
- 提供丰富的输入验证方法
二、创建Scanner对象
2.1 从标准输入(控制台)创建
Scanner scanner = new Scanner(System.in);
这是最常见的用法,用于从控制台读取用户输入。
2.2 从字符串创建
String input = "Hello World 123 45.67 true";
Scanner scanner = new Scanner(input);
2.3 从文件创建
try {
Scanner fileScanner = new Scanner(new File("data.txt"));
// 使用文件扫描器...
fileScanner.close(); // 记得关闭
} catch (FileNotFoundException e) {
e.printStackTrace();
}
注意:文件扫描器使用后必须关闭,推荐使用try-with-resources:
try (Scanner fileScanner = new Scanner(new File("data.txt"))) {
// 自动关闭
}
2.4 从其他输入流创建
InputStream inputStream = socket.getInputStream();
Scanner socketScanner = new Scanner(inputStream);
三、基本输入方法
3.1 读取基本数据类型
Scanner提供了一系列nextXxx()
方法:
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数: ");
int num = scanner.nextInt();
System.out.print("请输入一个小数: ");
double decimal = scanner.nextDouble();
System.out.print("请输入true或false: ");
boolean bool = scanner.nextBoolean();
注意:这些方法会阻塞程序,等待用户输入。
3.2 读取字符串
// 读取单个单词(以空白符分隔)
String word = scanner.next();
// 读取整行(包括空格)
String line = scanner.nextLine();
重要区别:
next()
:读取下一个标记(默认以空白符分隔)nextLine()
:读取当前行的剩余内容(包括换行符前的所有字符)
3.3 混合输入的问题与解决
常见问题:
System.out.print("请输入年龄: ");
int age = scanner.nextInt();
System.out.print("请输入姓名: ");
String name = scanner.nextLine(); // 这里会直接跳过!
原因:nextInt()
只读取数字,不读取行尾的换行符,导致nextLine()
读取到空字符串。
解决方案:
- 统一使用
nextLine()
然后转换:
int age = Integer.parseInt(scanner.nextLine());
- 或者在
nextInt()
后添加一个额外的nextLine()
:
int age = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
String name = scanner.nextLine();
四、高级输入处理
4.1 分隔符控制
默认情况下,Scanner使用空白符作为分隔符,但可以自定义:
String input = "one,two,three,four";
Scanner scanner = new Scanner(input);
scanner.useDelimiter(","); // 设置逗号为分隔符
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
// 输出:
// one
// two
// three
// four
4.2 使用正则表达式匹配
Scanner支持强大的正则表达式功能:
String input = "1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input);
s.findInLine("(\\d+) fish (\\d+) fish (\\w+) fish (\\w+) fish");
MatchResult result = s.match();
for (int i = 1; i <= result.groupCount(); i++) {
System.out.println(result.group(i));
}
// 输出:
// 1
// 2
// red
// blue
4.3 输入验证
在读取前检查输入类型:
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个整数: ");
while (!scanner.hasNextInt()) {
System.out.println("输入的不是整数!请重新输入: ");
scanner.next(); // 消耗无效输入
}
int num = scanner.nextInt();
常用检查方法:
hasNext()
:是否有更多标记hasNextInt()
:是否有整数hasNextDouble()
:是否有浮点数hasNextLine()
:是否有下一行hasNext(pattern)
:是否匹配指定正则表达式
五、Scanner的实用技巧
5.1 读取多个值
System.out.println("请输入三个数字,用空格分隔:");
double a = scanner.nextDouble();
double b = scanner.nextDouble();
double c = scanner.nextDouble();
System.out.println("平均值: " + (a + b + c) / 3);
5.2 处理CSV文件
try (Scanner csvScanner = new Scanner(new File("data.csv"))) {
csvScanner.useDelimiter(",|\r\n|\n"); // 逗号或换行符分隔
while (csvScanner.hasNext()) {
String name = csvScanner.next();
int age = csvScanner.nextInt();
double score = csvScanner.nextDouble();
System.out.printf("%s (年龄: %d, 分数: %.2f)%n", name, age, score);
}
}
5.3 读取密码(隐藏输入)
虽然Scanner不适合直接读取密码(因为输入可见),但可以结合Console类:
Console console = System.console();
if (console != null) {
char[] password = console.readPassword("请输入密码: ");
// 处理密码...
Arrays.fill(password, ' '); // 安全清除
} else {
// 在IDE中可能无法获取Console,回退到Scanner
System.out.print("请输入密码: ");
String password = new Scanner(System.in).nextLine();
// 处理密码...
}
六、常见问题与解决方案
6.1 输入不匹配异常
try {
int num = scanner.nextInt();
} catch (InputMismatchException e) {
System.out.println("请输入有效的整数!");
scanner.next(); // 消耗无效输入
}
6.2 资源泄漏问题
错误做法:
Scanner scanner = new Scanner(new File("data.txt"));
// 使用后忘记关闭
正确做法:
try (Scanner scanner = new Scanner(new File("data.txt"))) {
// 自动关闭
}
6.3 多线程问题
Scanner不是线程安全的。在多线程环境中,应该为每个线程创建独立的Scanner实例或进行同步控制。
七、性能优化建议
-
缓冲大型文件:对于大文件,可以包装在BufferedReader中
Scanner fileScanner = new Scanner(new BufferedReader(new FileReader("bigfile.txt")));
-
重用Scanner对象:避免频繁创建和销毁
-
选择合适的输入源:根据数据来源选择最合适的构造方法
-
及时关闭资源:使用try-with-resources确保资源释放
八、实际应用案例
8.1 控制台计算器
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("简单计算器 (输入q退出)");
while (true) {
System.out.print("> ");
if (!scanner.hasNextLine()) break;
String input = scanner.nextLine().trim();
if (input.equalsIgnoreCase("q")) break;
try {
String[] parts = input.split("\\s+");
if (parts.length != 3) {
System.out.println("格式: 数字 运算符 数字");
continue;
}
double a = Double.parseDouble(parts[0]);
String op = parts[1];
double b = Double.parseDouble(parts[2]);
double result;
switch (op) {
case "+": result = a + b; break;
case "-": result = a - b; break;
case "*": result = a * b; break;
case "/": result = a / b; break;
default: throw new IllegalArgumentException("未知运算符");
}
System.out.printf("结果: %.2f%n", result);
} catch (Exception e) {
System.out.println("错误: " + e.getMessage());
}
}
}
8.2 学生成绩统计
try (Scanner scanner = new Scanner(new File("scores.txt"))) {
int count = 0;
double sum = 0;
double max = Double.MIN_VALUE;
double min = Double.MAX_VALUE;
while (scanner.hasNextDouble()) {
double score = scanner.nextDouble();
count++;
sum += score;
max = Math.max(max, score);
min = Math.min(min, score);
}
System.out.println("统计结果:");
System.out.println("学生人数: " + count);
System.out.printf("平均分: %.2f%n", sum / count);
System.out.println("最高分: " + max);
System.out.println("最低分: " + min);
}
8.3 配置文件解析
# config.properties
username=admin
timeout=30
cache.enabled=true
Map<String, String> config = new HashMap<>();
try (Scanner scanner = new Scanner(new File("config.properties"))) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (line.isEmpty() || line.startsWith("#")) continue;
String[] keyValue = line.split("=", 2);
if (keyValue.length == 2) {
config.put(keyValue[0].trim(), keyValue[1].trim());
}
}
}
System.out.println("用户名: " + config.get("username"));
System.out.println("超时: " + Integer.parseInt(config.get("timeout")));
System.out.println("缓存启用: " + Boolean.parseBoolean(config.get("cache.enabled")));
九、总结
Scanner是Java中处理输入数据的强大工具,通过本文,你应该已经掌握了:
- 多种输入源处理:控制台、文件、字符串等
- 数据类型转换:各种基本类型的读取方法
- 高级解析技巧:分隔符控制、正则表达式匹配
- 常见问题解决:输入验证、资源管理、多线程问题
- 实际应用案例:计算器、成绩统计、配置文件解析
Scanner的最佳实践:
- 始终验证输入:使用
hasNextXxx()
方法 - 正确处理混合输入:注意
nextLine()
的行为 - 及时关闭资源:使用try-with-resources语句
- 合理选择输入源:根据场景选择最合适的构造方法
掌握了Scanner,你将能够轻松处理各种输入场景,使你的Java程序更加健壮和用户友好。无论是简单的控制台应用还是复杂的文件处理,Scanner都能成为你的得力助手。