使用BufferedReader.readLine()读取文本文件速度过慢

3

我正在尝试读取一个包含大约1000行非常长的文本文件。整个文件大小约为1.4MB。 我正在使用BufferedReader的readLine方法来读取文件。情况是需要花费8-10秒才能在控制台上输出结果。我尝试使用php的fgets完成相同的操作,它在眨眼之间打印出所有相同的行!!!这怎么可能呢? 以下是我正在使用的代码

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ClickLogDataImporter {
    public static void main(String [] args) {
        try {
            new ClickLogDataImporter().getFileData();
        } catch (Exception ex) {
            Logger.getLogger(ClickLogDataImporter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void getFileData() throws FileNotFoundException, IOException {
        String path = "/home/shantanu/Documents";
        BufferedReader br = new BufferedReader(new InputStreamReader(
                new FileInputStream(path+"/sample.txt")));
        String line = "";
        while((line = (br.readLine())) != null) {
            System.out.println(line);
        }

    }
}

PHP代码

<?php
    $fileName = "/home/shantanu/Documents/sample.txt";
    $file = fopen($fileName, 'r');
    while(($line = fgets($file)) != false) {
        echo $line."\n";
    }
?>

请为我解释一下这个问题。

1
请检查编辑后的代码。正如DJon所解释的那样,我在执行两个不可比较的过程。逐行阅读和倾倒整个内容是两个不同的过程。因此,我进行了另一个测试,现在使用fgets。结果仍然是相同的。 - Shades88
我假设这两个程序以相同的方式运行,也都是通过命令行运行的? - Danubian Sailor
是的,都在命令行上。 - Shades88
好的,那么你是在什么时候开始计时的呢?我在代码中没有看到这一点。因此问题可能出在JRE加载时间上,而不是你的代码执行时间上。 - Danubian Sailor
好的,当我注释掉控制台输出时,Java 大约需要 80-90 毫秒,而 PHP 最多只需要不到 5 毫秒!我进行了双重检查。但是就是这样。 - Shades88
所以这可能是加载System.out.println后面的类所需的时间。您应该先执行初始的System.out.prinlnt(),然后再启动计时器。 - Danubian Sailor
6个回答

0

我不确定,但我认为PHP只是根据您使用的方法打印文件,Java读取文件并从中获取每行内容,这意味着检查每个字符以查找换行符,该过程似乎完全不同。

string file_get_contents

如果您尝试使用PHP逐行打印文件中的每一行,速度会比较慢。

请检查编辑后的代码。现在我在 PHP 中使用 fgets。因此逐行读取所有行。结果没有变化。 - Shades88
你是在浏览器还是Shell中使用PHP?那么这一定是你的控制台输出,移除 System.out.printl(line) 然后看看需要多长时间。 - Djon
我正在从 shell 中运行。 - Shades88
如果您能提供样本文件,或告诉我们文件大小和行数,我可以测试两种方法并告诉您是否得到相同的结果。 - Djon
我创建了一个包含1000行1000个零的文件,文件大小为978 KB,在Netbeans控制台上加载时间为0毫秒,打印时间为1404毫秒。 - Djon
显示剩余2条评论

0

file_get_contents将所有文件内容加载到字符串中,而使用Java代码时,您是逐行读取和打印的。 如果您在像Eclipse这样的IDE中进行测试,则控制台输出可能会非常缓慢。 如果您想要file_get_contents的确切行为,可以使用此不太优雅的代码:

 File f = new File(path, "sample.txt");
 ByteArrayOutputStream bos = new ByteArrayOutputStream(new Long(Math.min(Integer.MAX_VALUE, f.length())).intValue());
 FileInputStream fis = new FileInputStream(f);
 byte[] buf = new byte[1024 * 8];
 int size;
 while((size = fis.read(buf)) > 0) {
    bos.write(buf, 0, size);
 }
 fis.close();
 bos.close();
 System.out.println(new String(bos.toByteArray()));

0

对于那段代码来说,8秒听起来太长了。老实说,我怀疑其他问题正在发生。你确定不是控制台输出需要很长时间吗?

我建议你计时(例如使用System.nanoTime),在结束时写出总时间,但将控制台最小化运行。我猜想你会发现它足够快。


两个代码都在控制台打印行。尽管如此,PHP 仍然是最强的! - Shades88
@Shades88:也许是因为PHP在某些情况下可以更快地访问控制台。我非常怀疑时间是用来读取文件的。这很容易测试。如果你在Java中去掉控制台输出 - 比如只计算你读取的行数并在最后输出该计数 - 需要多长时间? - Jon Skeet
我进行了相同的测试。注释掉控制台输出,Java需要80-90毫秒,然而PHP只需要3-5毫秒!! - Shades88
@Shades88:我猜这时候取决于操作系统缓存和JIT编译。你可能会发现,如果你运行该方法多次,它会非常快。不过,这个程序是否代表你正在尝试做的任何事情,或者你只是想比较PHP和Java的速度?如果是这样,我认为这并不是一个非常有用的比较。 - Jon Skeet

0

这只是控制台输出慢吗?既然你知道文件已经正确读取了,试试把System.out.println(line);这行注释掉。


0

如果你使用readline ,它将为每一行读取文件1000次。尝试使用具有非常大的缓冲区(如28000或更多)的read函数。然后,对于1.4 MB的文件,它将总共读取60次,比1000少得多。如果你使用小缓冲区(如1000),那么它将大约读取1300次左右的文件,这比1000还要慢(readline)。同时,在打印行时,使用print而不是println,因为这些行不是确切的行,而是字符数组。


-1

读取器通常很慢,你应该尝试使用快速的流读取器。并确保文件打开过程不会花费太多时间。如果文件已经打开并创建了流对象,然后测量时间,那么你就可以确定是由于文件打开问题还是读取文件问题。确保在此操作时系统io负载不高,否则你的测量结果将不准确。

 BufferedInputStream reader=new BufferedInputStream(new FileInputStream("/home/shantanu/Documents/sample.txt"));
 byte[] line=new byte[1024];
 while(reader.read(line)>0) {
 System.out.println(new String(line));
 }

这仍然是在做同样的工作 - 将二进制数据转换为文本数据 - 只是在不同的位置。此外,建议使用 InputStream 处理文本数据基本上是一个坏主意。例如,您的代码可能最终读取部分字符(例如使用 UTF-8 的两个字节字符的第一个字节),而您永远不会知道。更重要的是,您的代码没有使用 read 的返回值,因此无论缓冲区是否充满有用的数据,您都将使用整个缓冲区创建字符串。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接