Java——从文件中读取。输入流与读取器

Java——从文件中读取。输入流与读取器,java,text,inputstream,Java,Text,Inputstream,在我看到的每个从文件读取的Java实现中,我几乎总是看到一个用于逐行读取的文件读取器。我的想法是,这将是非常低效的,因为它需要每行系统调用 我一直在做的是使用输入流并直接获取字节。在我的实验中,这要快得多。我的测试是一个1MB的文件 //Stream method try { Long startTime = new Date().getTime(); InputStream is = new FileInputStream("test");

在我看到的每个从文件读取的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
那么,为什么人们使用阅读器而不是流呢


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

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

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

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


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

BufferedReader br = new BufferedReader(new FileReader("test"),2000000);
如果选择正确的缓冲区大小,则速度会更快

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

选择使用
InputStream
Reader
不取决于性能。通常在读取文本数据时使用
Reader
,因为使用Reader可以更轻松地处理字符集

另一点,你的代码在这里

byte[] b = new byte[is.available()];
is.read(b);
String text = new String(b);
这是不对的。说明

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


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

你在把苹果比作香蕉。与尽可能快地抓取数据相比,即使使用bufferedReader,一次读取一行数据的效率也会更低。请注意,不鼓励使用available,因为它并非在所有情况下都准确。我在开始使用密码流时发现了这一点。

您的代码不正确。不能保证它会读取整个文件,请参阅read和available方法的文档。您是否尝试过package的文件。readAllLines(…)方法。+1了解一些非常有趣的新知识。从本地文件系统中存在的纯文本文件中读取时是否存在危险?@Jeremy为整个流分配缓冲区是不正确的。@Jeffrey如果您有,我想看看您在这方面的任何资源。在此之前,我一直很高兴地使用available,没有遇到任何问题。我相信你,但我想知道是否真的有一个合适的情况。@Jeremy阅读了文档。我在上一句话中或多或少引用了文档的第二段。@Jeremy
可用的问题在于它只能返回可用的字节数而不阻塞。如果您100%确定
InputStream
的缓冲区包含整个文件,并且
InputStream
将从
可用的
返回正确的数字,那么请务必使用它。但是,如果您的文件大于
InputStream
s缓冲区,或者您的
InputStream
没有返回正确的数字,则使用它将失败。手动提供缓冲区大小似乎只会对我的性能产生负面影响。您的文件有多大?您为JVM投入了多少堆?