You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scanner读取完文件后重新从头读取内容的最优实现方案

Scanner读取完文件后重新从头读取内容的最优实现方案

嗨,我来帮你解决这个Scanner重读文件的问题~

首先得明白为啥你现在的代码里edgesSet啥都读不到:Scanner是基于流工作的,一旦你用hasNextLine()读到文件末尾,流的“指针”就停在那儿了,没法自动回到开头。那咱们有几种靠谱的方案来解决这个问题,我给你挨个分析:

方案1:重新创建Scanner(简单直接,大多数场景首选)

你提到的“每次新建Scanner”其实完全没问题,真的不用觉得这是“笨办法”!Scanner本身的创建开销非常小,尤其是针对小文件来说,几乎可以忽略不计。而且这种方式代码最直观,后续维护起来也省心。

你只需要调整main方法,每次处理完一个函数就关闭当前Scanner,再新建一个就行——用try-with-resources语法还能自动帮你关闭资源,避免内存泄漏:

public class Test{
    public static void main(String[] args) {
        File fileName = new File("test.txt");
        try {
            // 第一次读取顶点,用完自动关闭Scanner
            try (Scanner scanner = new Scanner(fileName)) {
                verticesSet(scanner);
            }
            // 第二次读取边,新建一个Scanner从头读
            try (Scanner scanner = new Scanner(fileName)) {
                edgesSet(scanner);
            }
        } catch (FileNotFoundException exception) {
            System.err.println("file not found: " + exception.getMessage());
        }
    }

    // 下面的verticesSet和edgesSet函数保持不变
    public static void verticesSet(Scanner scanner) {
        Set<Integer> verticesSet = new HashSet<>();
        while (scanner.hasNextLine()) {
            verticesSet.add(scanner.nextInt());
        }
        System.out.println(verticesSet);
    }

    public static void edgesSet(Scanner scanner) {
        List<String> edges = new ArrayList<>();
        while (scanner.hasNextLine()) {
            edges.add(scanner.nextLine());
        }
        for (int i = 0; i < edges.size(); i++) {
            System.out.println("index" + i + " is" + edges.get(i));
        }
    }
}

就算你有10个函数要处理这个文件,每次新建Scanner也不会有性能问题,反而代码逻辑清晰,不容易出错。

方案2:先把文件内容读到内存(适合多次重复处理的场景)

如果你的文件不大,而且需要反复读取内容,那可以先把整个文件的内容一次性读到内存里(比如一个List<String>),之后每次处理都直接用内存里的数据,不用反复打开文件,能减少IO开销。

比如这样修改:

public class Test{
    public static void main(String[] args) {
        File fileName = new File("test.txt");
        try {
            // 一次性把所有行读到内存里
            List<String> allFileLines = Files.readAllLines(fileName.toPath());
            
            // 处理顶点:把内存里的行拼成字符串,创建Scanner
            try (Scanner scanner = new Scanner(String.join(System.lineSeparator(), allFileLines))) {
                verticesSet(scanner);
            }
            
            // 处理边:直接用内存里的列表,甚至可以不用Scanner
            edgesSet(allFileLines);
            
        } catch (IOException exception) {
            System.err.println("file error: " + exception.getMessage());
        }
    }

    public static void verticesSet(Scanner scanner) {
        Set<Integer> verticesSet = new HashSet<>();
        while (scanner.hasNextLine()) {
            verticesSet.add(scanner.nextInt());
        }
        System.out.println(verticesSet);
    }

    // 修改edgesSet直接接收List<String>,更高效
    public static void edgesSet(List<String> edges) {
        for (int i = 0; i < edges.size(); i++) {
            System.out.println("index" + i + " is" + edges.get(i));
        }
    }
}

这种方式的好处是只打开一次文件,后续操作都是在内存里完成,速度更快;缺点是如果文件特别大(比如几百MB以上),会占用较多内存,这时候就不适合了。

方案3:用流的mark/reset(不推荐,太麻烦)

还有一种技术上可行但不太实用的方法:用BufferedInputStream包装文件流,通过mark()标记文件开头,reset()回到开头。但这种方式需要处理流的缓冲区大小,而且Scanner本身没有重置方法,需要重新创建Scanner绑定到重置后的流上,代码会复杂很多,除非你有特殊需求,否则不建议用。

总结一下

  • 如果只是少数几次读取文件,**方案1(重新创建Scanner)**是最优选择,简单又可靠;
  • 如果需要多次重复处理文件内容,且文件不大,**方案2(预读到内存)**更高效;
  • 方案3尽量不用,太折腾,性价比低。

备注:内容来源于stack exchange,提问作者user23819755

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:38:09