getline在读取文件时读取整个文件然后根据换行符分割

时间:2013-01-22 16:24:53

标签: c++

我想现在处理硬盘上文件的每一行。是否更好地加载整个文件,然后根据换行符(使用boost)进行拆分,还是使用getline()更好?我的问题是getline()在调用时读取单行(导致多个硬盘访问)或读取整个文件并逐行提供?

6 个答案:

答案 0 :(得分:5)

getline会将read()作为系统调用,在C库的最深处。它被调用的次数及其调用方式取决于C库设计。但是很可能一次读取一行与整个文件没有明显的区别,因为底层的操作系统一次会读取(至少)一个磁盘块,而且很可能至少是一个“页面” “(4KB),如果不是更多。

此外,unles你读完之后几乎没有用你的字符串做任何事情(例如你写的是“grep”之类的东西,所以大多只是读取字符串),读取一行的开销不太可能你花费的时间很长一段时间。

但是“一次加载整个文件”有几个不同的问题:

  1. 在阅读完整个文件之前,您不会开始处理。
  2. 你需要足够的内存来将整个文件读入内存 - 如果文件的大小是几百GB怎么办?那你的程序失败了吗?
  3. 除非您使用分析来证明代码运行缓慢的原因,否则不要尝试优化某些内容。你只会给自己造成更多问题。

    编辑:所以,我编写了一个程序来衡量这一点,因为我觉得它非常有趣。

    结果非常有趣 - 为了使比较公平,我创建了三个1297984192字节的大文件(通过使用大约十二个不同的源文件复制目录中的所有源文件,然后将此文件复制多次到“乘以”它,直到花了超过1.5秒来运行测试,这是我认为你需要多长时间来运行以确保时间不易受随机“网络数据包进入”或其他一些外部影响的影响花时间走出这个过程)。

    我还决定按流程测量系统和用户时间。

    $ ./bigfile
    Lines=24812608
    Wallclock time for mmap is 1.98 (user:1.83 system: 0.14)
    Lines=24812608
    Wallclock time for getline is 2.07 (user:1.68 system: 0.389)
    Lines=24812608
    Wallclock time for readwhole is 2.52 (user:1.79 system: 0.723)
    $ ./bigfile
    Lines=24812608
    Wallclock time for mmap is 1.96 (user:1.83 system: 0.12)
    Lines=24812608
    Wallclock time for getline is 2.07 (user:1.67 system: 0.392)
    Lines=24812608
    Wallclock time for readwhole is 2.48 (user:1.76 system: 0.707)
    

    这里有三个不同的函数来读取文件(当然,还有一些代码可以测量时间和内容,但是为了减少这篇文章的大小,我选择不发布所有这些 - 而且我玩了订购看看是否有任何不同,所以上面的结果与这里的函数的顺序不一样)

    void func_readwhole(const char *name)
    {
        string fullname = string("bigfile_") + name;
        ifstream f(fullname.c_str());
    
        if (!f) 
        {
            cerr << "could not open file for " << fullname << endl;
            exit(1);
        }
    
        f.seekg(0, ios::end);
        streampos size = f.tellg();
    
        f.seekg(0, ios::beg);
    
        char* buffer = new char[size];
        f.read(buffer, size);
        if (f.gcount() != size)
        {
            cerr << "Read failed ...\n";
            exit(1);
        }
    
        stringstream ss;
        ss.rdbuf()->pubsetbuf(buffer, size);
    
        int lines = 0;
        string str;
        while(getline(ss, str))
        {
            lines++;
        }
    
        f.close();
    
    
        cout << "Lines=" << lines << endl;
    
        delete [] buffer;
    }
    
    void func_getline(const char *name)
    {
        string fullname = string("bigfile_") + name;
        ifstream f(fullname.c_str());
    
        if (!f) 
        {
            cerr << "could not open file for " << fullname << endl;
            exit(1);
        }
    
        string str;
        int lines = 0;
    
        while(getline(f, str))
        {
            lines++;
        }
    
        cout << "Lines=" << lines << endl;
    
        f.close();
    }
    
    void func_mmap(const char *name)
    {
        char *buffer;
    
        string fullname = string("bigfile_") + name;
        int f = open(fullname.c_str(), O_RDONLY);
    
        off_t size = lseek(f, 0, SEEK_END);
    
        lseek(f, 0, SEEK_SET);
    
        buffer = (char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, f, 0);
    
    
        stringstream ss;
        ss.rdbuf()->pubsetbuf(buffer, size);
    
        int lines = 0;
        string str;
        while(getline(ss, str))
        {
            lines++;
        }
    
        munmap(buffer, size);
        cout << "Lines=" << lines << endl;
    }
    

答案 1 :(得分:2)

操作系统将读取整个数据块(取决于磁盘的格式化,通常一次为4-8k)并为您进行一些缓冲。让操作系统为您处理,并以对您的程序有意义的方式读取数据。

答案 2 :(得分:1)

fstreams合理缓冲。操作系统对硬盘的基础过程得到了合理的缓冲。硬盘本身有一个合理的缓冲区。如果逐行读取文件,您肯定会触发更多的硬盘访问。或者就个性而言。

因此没有理由将整个文件加载到一个大缓冲区并在该缓冲区上工作,因为它已经在缓冲区中。并且通常没有理由一次缓冲一行。为什么要分配内存来缓冲已经在ifstream中缓冲的字符串中的内容?如果可以的话,直接处理流,不要费心从一个缓冲区到下一个缓冲区扔两次或更多。除非它支持可读性和/或你的探查器告诉你光盘访问正在大大减慢你的程序。

答案 3 :(得分:0)

如果它是磁盘上的一个小文件,那么读取整个文件并逐行解析而不是一次读取一行可能会更有效 - 这将占用大量磁盘访问权。

答案 4 :(得分:0)

我相信C ++习惯用法是逐行读取文件,并在读取文件时创建基于行的容器。很可能iostreams(getline)将被缓冲到足以让您注意不到显着差异。

但是对于非常大的文件,您可以通过读取较大的文件块(而不是一次整个文件)并在找到换行符时拆分内部来获得更好的性能。

如果您想明确了解哪种方法更快以及费用多少,则必须对代码进行分析。

答案 5 :(得分:0)

如果可以在内存中容纳所有数据,它会更好。因为无论何时请求I / O,程序都会丢失处理并等待Q.

enter image description here

但是,如果文件大小很大,那么最好一次读取处理所需的数据。因为较大的读操作将花费很多时间来完成小的操作。 cpu进程切换时间比整个文件读取时间小得多。