C++ 读取文件时获取行,而不是读取整个文件,然后根据换行符进行拆分

C++ 读取文件时获取行,而不是读取整个文件,然后根据换行符进行拆分,c++,C++,我现在想处理硬盘上文件的每一行。加载一个文件作为一个整体,然后根据换行符进行拆分(使用boost)更好,还是使用getline()更好?我的问题是getline()调用时是否读取单行(导致多个硬盘访问)或读取整个文件并逐行给出?操作系统将读取整个数据块(取决于磁盘的格式化方式,通常一次读取4-8k),并为您执行一些缓冲。让操作系统替您处理,并以对您的程序有意义的方式读取数据。如果是磁盘上的一个小文件,读取整个文件并逐行解析它可能更有效,这会占用大量磁盘访问量。 < P>我相信C++习惯是逐行读取

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

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

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

< P>我相信C++习惯是逐行读取文件,并在读取文件时创建一个基于行的容器。很可能iostreams(
getline
)将得到足够的缓冲,您不会注意到显著的差异

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


如果你想明确知道哪种方法更快,速度有多快,你就必须分析你的代码。

如果所有的数据都可以放在内存中,那么最好获取这些数据,因为每当你请求I/O时,你的程序就会放松处理,并进入等待Q


但是,如果文件大小很大,则最好一次读取处理过程中所需的尽可能多的数据。因为较大的读取操作要比较小的读取操作花费更多的时间才能完成。cpu进程切换时间比整个文件读取时间小得多。

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

此外,除非您在读取字符串后几乎什么也不做(例如,您正在编写类似“grep”的内容,因此大多数情况下只是读取字符串来查找字符串),否则每次读取一行的开销不太可能是您所花费时间的很大一部分

但“一次性加载整个文件”有几个明显的问题:

  • 在读取整个文件之前,不会开始处理
  • 您需要足够的内存将整个文件读入内存-如果文件大小为几百GB怎么办?那么你的程序失败了吗 不要试图优化某些东西,除非您已经使用分析来证明这是代码运行缓慢的原因之一。你只是给自己制造了更多的问题

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

    结果确实很有趣——为了公平比较,我创建了三个大文件,每个文件大小为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\u readwhole(常量字符*名称)
    {
    字符串全名=字符串(“大文件”)+名称;
    ifstream f(fullname.c_str());
    如果(!f)
    {
    
    cerrfstreams被合理地缓冲。操作系统对硬盘的底层访问被合理地缓冲。硬盘本身有一个合理的缓冲区。如果你逐行或逐字读取文件,你肯定不会触发更多的硬盘访问


    因此,没有理由将整个文件加载到一个大的缓冲区中并处理该缓冲区,因为它已经在一个缓冲区中。而且通常也没有理由一次缓冲一行。为什么要分配内存来缓冲已在ifstream中缓冲的字符串中的某个内容?如果可以,直接处理该流,不用费心去处理从一个缓冲区到下一个缓冲区,每件事都会发生两次或两次以上。除非它支持可读性和/或您的档案器告诉您,磁盘访问会显著降低程序的速度。

    我想处理整个文件,我的问题是关于速度,哪种技术基本上更快。您无法分辨速度的差异。操作的系统非常擅长缓存/缓冲。只要你不在两个不同的文件之间交替访问,你就可以了。如果你不相信我-自己试试。实际上,硬件访问的数量让我有些担心,我已经在getline的基础上编写了代码,但如果读取的数量非常高,硬件将很快被损坏ner所以我的问题是,通过其他技术读取硬盘驱动器的数量是否会有任何差异?或者,在硬件级别,这是相同的事情。每个数据块都将被读取一次。除非您同时访问另一个文件,否则会有最小的差异-现代硬件有大量缓存来提高访问速度。通常情况下,它会重新启动
    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;
    }