正确,高效的文件读取

1''*_*1'' 2 c c++ file-io

我想一次一个地从CSV文件的第一行读取和处理(例如打印)条目.我假设Unix风格的\n换行符,没有条目超过255个字符,并且(现在)在EOF之前有一个换行符.这意味着是一个更有效的替代fgets(),接着strtok().

#include <stdio.h>
#include <string.h>

int main() {
    int i;
    char ch, buf[256];
    FILE *fp = fopen("test.csv", "r");

    for (;;) {
        for (i = 0; ; i++) {
            ch = fgetc(fp);
            if (ch == ',') {
                buf[i] = '\0'; 
                puts(buf);
                break;
            } else if (ch == '\n') {
                buf[i] = '\0'; 
                puts(buf);
                fclose(fp);
                return 0;
            } else buf[i] = ch;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 这种方法是否尽可能高效和正确?
  2. 使用此方法测试EOF和文件读取错误的最佳方法是什么?(可能性:测试针对字符宏EOF,feof(),ferror()等等).
  3. 我可以使用C++文件I/O执行相同的任务而不会降低效率吗?

lea*_*der 5

最有效的是在很大程度上取决于操作系统,标准库(例如libc),甚至是您运行的硬件.这使得几乎不可能告诉你什么是"最有效"的.

话虽如此,你可以尝试一些事情:

  • 使用mmap()或本地操作系统等效(Windows具有CreateFileMapping/OpenFileMapping/MapViewOfFile,可能还有其他).然后你不做显式的文件读取:你只需要访问文件,就好像它已经在内存中一样,任何不存在的文件都会被页面错误机制搞砸.
  • 手动将整个文件读入缓冲区,然后处理该缓冲区.调用文件读取函数的次数越少,您执行的函数调用开销就越少,并且可能还会减少应用程序/操作系统域切换.显然这会占用更多内存,但可能非常值得.
  • 为您的问题和平台使用更优化的字符串扫描程序.逐个字符地进行自己几乎从来没有像依靠现有的与您的问题域接近的东西一样快.例如,您可以打赌,strchr并且memchr可能比大多数代码都更好地进行优化,您可以自己动手操作,例如一次读取整个缓存行或单词,使用更好的算法进行扫描等等.对于更复杂的情况,您可能会考虑一个完整的正则表达式引擎,可以将您的正则表达式编译为快速复杂的情况.
  • 避免复制你的字符串.根据"查找分隔符"然后"分隔符之间的输出"来思考可能会有所帮助.例如,您可以使用strchr查找下一个感兴趣的字符,然后fwrite直接从输入缓冲区写入stdout.然后,您将大部分工作保留在几个本地寄存器中,而不是使用堆栈或堆buf.

但是,如果有疑问,请尝试一些可能性和个人资料,个人资料,个人资料.

同样对于这类问题,要非常了解由操作系统和硬件缓存引起的运行之间的差异:在每次更改后分析一堆运行而不是仅运行一次 - 如果可能的话,使用可能总是命中缓存的测试(如果您正在尝试测量最佳情况的性能)或可能会错过的测试(如果您正在尝试测量最坏情况的性能).


关于C++文件IO(fstream等等),请注意它们是更大,更复杂的野兽.它们往往包括诸如区域设置管理,自动缓冲等之类的东西 - 并且不易受特定类型的编码错误的影响.

如果你正在做一些非常简单的事情(就像你在这里描述的那样),我倾向于发现C++库的东西会受到阻碍.(通过字符串流方法使用调试器和"步骤指令",而不是某些C字符串函数,你会很快得到这种感觉.)

这一切都取决于您将来是否想要或需要额外的功能或安全性.


最后,强制性的"不要让小东西流汗".如果真的很重要的话,只花时间在这里进行优化.否则,请相信图书馆和操作系统会在大多数时间为您做正确的事情 - 如果您对微观优化过于苛刻,您会发现自己在以后拍摄自己.这并不是为了阻止你思考"我是否应该提前阅读整个文件,是否会破坏未来的用例" - 因为这是宏观,而不是微观.

但一般来说,如果你没有做出这种"让它更快"的调查有充分的理由 - 即"我需要这个应用程序才能更好地执行它,而且这个代码在profiler中表现得很慢" ,或"为了好玩而这样做,这样我就能更好地理解系统" - 好吧,先把时间花在别的地方.=)