【问题标题】:Java -- reading from a file. Input stream vs. readerJava——从文件中读取。输入流与阅读器
【发布时间】:2012-05-03 10:25:04
【问题描述】:

在我看到的从文件读取的每个 Java 实现中,我几乎总是看到一个文件读取器用于逐行读取。我的想法是,这将非常低效,因为它需要每行一个系统调用。

我一直在做的是使用输入流并直接获取字节。在我的实验中,这明显更快。我的测试是一个 1MB 的文件。

    //Stream method
    try {
        Long startTime = new Date().getTime();

        InputStream is = new FileInputStream("test");
        byte[] b = new byte[is.available()];
        is.read(b);
        String text = new String(b);
        //System.out.println(text);

        Long endTime = new Date().getTime();
        System.out.println("Text length: " + text.length() + ", Total time: " + (endTime - startTime));

    }
    catch (Exception e) {
        e.printStackTrace();
    }

    //Reader method
    try {
        Long startTime = new Date().getTime();

        BufferedReader br = new BufferedReader(new FileReader("test"));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) {
            sb.append(line);
            sb.append("\n");
        }
        String text = sb.toString();

        Long endTime = new Date().getTime();
        System.out.println("Text length: " + text.length() + ", Total time: " + (endTime - startTime));

    }
    catch (Exception e) {
        e.printStackTrace();
    }

这给出了以下结果:

Text length: 1054631, Total time: 9
Text length: 1034099, Total time: 22

那么,为什么人们使用阅读器而不是流?

如果我有一个方法可以获取一个文本文件并返回一个包含所有文本的字符串,那么使用流是否一定更好?

【问题讨论】:

  • 您的代码不正确。不保证会读取整个文件,请参阅 read 和 available 方法的文档。
  • 你试过java.nio.File包的Files.readAllLines(...)方法吗?
  • +1 表示学到了新东西

标签: java text inputstream


【解决方案1】:

尝试增加BufferedReader 缓冲区大小。例如:

BufferedReader br = new BufferedReader(new FileReader("test"),2000000);

如果你选择正确的缓冲区大小,你会更快。

然后在带有Reader 的示例中,您将花时间填充StringBuilder。如果需要处理行,则必须逐行读取文件。但是,如果您只需要读取字符串中的文本,则使用 public int read(char[] cbuf) 读取更大的文本块并将这些块写入以适当大小初始化的 StringWriter

选择使用InputStreamReader 不取决于性能。一般在阅读文本数据的时候使用Reader,因为有了阅读器可以更轻松地处理字符集。

还有一点,你的代码在这里

byte[] b = new byte[is.available()];
is.read(b);
String text = new String(b);

这是不正确的。 documentation 告诉

请注意,虽然 InputStream 的某些实现会返回流中的总字节数,但很多不会。使用该方法的返回值来分配一个用于保存该流中所有数据的缓冲区是不正确的。

所以注意,你需要修复它。

【讨论】:

  • 手动提供缓冲区大小似乎只会对我的性能产生负面影响。
  • 您的文件有多大?您为 JVM 分配了多少堆?
【解决方案2】:

您正在将苹果与香蕉进行比较。即使使用缓冲读取器,一次读取一行的效率也会低于尽可能快地获取数据。请注意,不鼓励使用 available,因为它在所有情况下都不准确。当我开始使用密码流时,我自己发现了这一点。

【讨论】:

  • 这很有趣。从本地文件系统上存在的纯文本文件中读取时可用有危险吗?
  • @Jeremy 使用available 为整个流分配缓冲区是不正确的。
  • @Jeffrey 如果你有它,我很想看看你有什么资源。在此之前,我一直很高兴地使用可用,没有遇到任何问题。我相信你,但我想知道是否真的存在合适的情况。
  • @Jeremy 阅读available 的文档。我在上一个声明中或多或少地引用了文档的第二段。
  • @Jeremy available 的问题是它只能返回可用的字节数没有阻塞。如果您 100% 确定您的 InputStream 的缓冲区包含您的整个文件,并且您的 InputStream 将从 available 返回正确的数字,那么请务必使用它。但是如果你的文件大于InputStreams 缓冲区或者你的InputStream 没有返回正确的数字,使用它就会失败。
【解决方案3】:

FileReader 通常与BufferedReader 结合使用,因为通常逐行读取文件是有意义的,特别是如果文件具有明确定义的记录结构,其中每条记录对应于一行。

另外,FileReader 可以简化处理字符编码和转换的一些工作,如javadocs 所述:

读取字符文件的便利类。此类的构造函数假定默认字符编码和默认字节缓冲区大小是适当的...... FileReader 用于读取字符流。

【讨论】:

    猜你喜欢
    • 2014-07-11
    • 2014-10-31
    • 2017-08-21
    • 2013-07-16
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多