如何使用 Java Scanner 正确读取文件中的数字并求和

本文详解 scanner 读取文件数字时的常见误区,重点说明为何 `new scanner(path)` 无法直接替代 `new scanner(new file(path))`,并提供健壮、简洁、符合最佳实践的实现方案。

在 Java 中,Scanner 构造函数接受多种输入源(InputStream、Readable、File、Path、甚至 String),但传入字符串路径(如 "data.txt")与传入 File 对象有本质区别

  • ✅ new Scanner(new File("data.txt")):将 File 对象作为数据源,Scanner 会尝试打开该文件并读取其内容;
  • ❌ new Scanner("data.txt"):将字符串 "data.txt" 本身作为待解析的文本内容——Scanner 不会打开文件,而是直接对这个 8 字符字符串进行扫描,因此 hasNextInt() 永远返回 false,循环不执行,结果恒为 0。
? 补充说明:Java 7+ 确实支持 new Scanner(Paths.get("data.txt"))(需传入 java.nio.file.Path 对象),但这与直接传 String 路径完全不同。Scanner(String) 构造器仅用于解析内存中的字符串字面量。

以下是推荐的、健壮且简洁的实现方式(使用 try-with-resources + Path + Files.newInputStream(),兼容现代 Java 最佳实践):

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

public class SumNumbers {
    public static void main(String[] args) throws IOException { // 委托异常,避免空 catch
 

String path = "numbers.txt"; int sum = 0; try (Scanner s = new Scanner(Files.newInputStream(Paths.get(path)))) { while (s.hasNext()) { // 关键:不再用 hasNextInt() 控制循环 if (s.hasNextInt()) { sum += s.nextInt(); } else { s.next(); // 跳过非数字 token(如字母、符号、空行) } } } System.out.println("Sum: " + sum); } }

关键改进点说明:

  • 循环条件改为 while (s.hasNext()):确保遍历所有 token,再逐个判断是否为整数;
  • 移除冗余的 if (s.hasNextInt()) 判断:原代码中 while 已保证进入循环时 hasNextInt() 为真,内层 if 完全多余;
  • 异常处理升级为 throws IOException:避免吞掉关键错误信息(如文件不存在、权限不足、编码异常),便于调试与维护;
  • 优先使用 Paths.get() + Files.newInputStream():比 new File(path) 更现代、更安全(自动处理路径分隔符、支持符号链接等),且明确表达“以文件内容为输入源”的语义。

⚠️ 注意事项:

  • 若文件包含浮点数(如 3.14)、负数(如 -42)或十六进制数(如 0xFF),hasNextInt() 会跳过它们;如需支持,可改用 hasNextDouble() 或结合正则校验;
  • Scanner 默认使用空白符(空格、制表符、换行)分隔 token,无需额外处理换行;
  • 避免在生产环境使用 throws Exception —— 应精确声明 throws IOException,体现契约清晰性。

掌握这一模式,不仅能正确求和,更能写出可读、可维护、符合 Java 生态演进方向的 I/O 代码。