【问题标题】:Does the Scanner class load the entire file into memory at once?Scanner 类是否一次将整个文件加载到内存中?
【发布时间】:2012-05-07 08:47:26
【问题描述】:

我经常使用 Scanner 类来读取文件,因为它非常方便。

      String inputFileName;
      Scanner fileScanner;

      inputFileName = "input.txt";
      fileScanner = new Scanner (new File(inputFileName));

我的问题是,上述语句是否一次将整个文件加载到内存中?或者对 fileScanner 进行后续调用,例如

      fileScanner.nextLine();

从文件中读取(即从外部存储而不是从内存中读取)?我问是因为我担心如果文件太大而无法一次全部读入内存会发生什么。谢谢。

【问题讨论】:

  • 答案是否定的。但它是按缓冲区读取文件的——这意味着以块的形式。

标签: java java.util.scanner


【解决方案1】:

对于大文件,最好使用 BufferedReaderFileReader 之类的东西。一个基本的例子可以在here找到。

【讨论】:

  • @Sheriff 请参阅 edalorzo 的回答。看来我阅读整个文件是错误的,但是我留下了答案,因为 Buffered + FileReader 确实可以更好地处理大文件。
  • @Aidanc - 你为什么这么说?当然,这取决于您是否需要 Scanner 的解析功能。当然,如果 OP 要使用 nextLine(),则 BufferedReader 可能会更快一些。 (请注意,OP 说“对 fileScanner 的后续调用 like fileScanner.nextLine()”...)
  • 为什么你认为 BufferedReader 更好?
【解决方案2】:

通过阅读代码,默认情况下它似乎每次加载 1 KB。对于长文本行,缓冲区的大小可能会增加。 (到你拥有的最长文本行的大小)

【讨论】:

    【解决方案3】:

    如果您阅读了源代码,您可以自己回答问题。

    似乎有问题的 Scanner 构造函数的实现显示:

    public Scanner(File source) throws FileNotFoundException {
            this((ReadableByteChannel)(new FileInputStream(source).getChannel()));
    }
    

    后者被包装到一个阅读器中:

    private static Readable makeReadable(ReadableByteChannel source, CharsetDecoder dec) {
        return Channels.newReader(source, dec, -1);
    }
    

    它是使用缓冲区大小读取的

    private static final int BUFFER_SIZE = 1024; // change to 1024;
    

    正如您在构造链中的最终构造函数中看到的那样:

    private Scanner(Readable source, Pattern pattern) {
            assert source != null : "source should not be null";
            assert pattern != null : "pattern should not be null";
            this.source = source;
            delimPattern = pattern;
            buf = CharBuffer.allocate(BUFFER_SIZE);
            buf.limit(0);
            matcher = delimPattern.matcher(buf);
            matcher.useTransparentBounds(true);
            matcher.useAnchoringBounds(false);
            useLocale(Locale.getDefault(Locale.Category.FORMAT));
        }
    

    因此,扫描仪似乎不会一次读取整个文件。

    【讨论】:

    • +1 不知道它没有一次读取整个文件。答案已编辑。但是,它仍然会遇到 BufferedReader + FileReader 不会遇到的较大文件的问题。
    • @Aidanc 什么样的问题?
    【解决方案4】:

    在 ACM 竞赛中,快速阅读非常重要。在 Java 中,我们发现使用类似的东西非常快......

        FileInputStream inputStream = new FileInputStream("input.txt");
        InputStreamReader streamReader = new InputStreamReader(inputStream, "UTF-8");
        BufferedReader in = new BufferedReader(streamReader);
        Map<String, Integer> map = new HashMap<String, Integer>();
        int trees = 0;
        for (String s; (s = in.readLine()) != null; trees++) {
            Integer n = map.get(s);
            if (n != null) {
                map.put(s, n + 1);
            } else {
                map.put(s, 1);
            }
        }
    

    在这种情况下,该文件包含树名...

    Red Alder
    Ash
    Aspen
    Basswood
    Ash
    Beech
    Yellow Birch
    Ash
    Cherry
    Cottonwood
    

    您可以使用StringTokenizer 来捕捉您想要的任何部分。

    如果我们对大文件使用Scanner,就会出现一些错误。从 10000 行的文件中读取 100 行!

    扫描器可以从任何实现 Readable 的对象中读取文本 界面。如果调用底层可读的 Readable.read(java.nio.CharBuffer) 方法抛出一个 IOException 然后 扫描器假定已经到达输入的末尾。这 底层可读的最近抛出的 IOException 可以是 通过 ioException() 方法检索。

    在 API 中告知

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-24
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多