C++ 正确、高效的文件读取

C++ 正确、高效的文件读取,c++,c,file-io,C++,C,File Io,我想从CSV文件的第一行一次读取和处理(例如打印)条目。我假设Unix风格的\n换行符,没有条目长度超过255个字符,并且(目前)在EOF之前有一个换行符。这意味着这是一种比fgets()后接strtok()更有效的替代方案 #包括 #包括 int main(){ int i; char-ch,buf[256]; 文件*fp=fopen(“test.csv”,“r”); 对于(;;){ 对于(i=0;i++){ ch=fgetc(fp); 如果(ch==','){ buf[i]='\0'; pu

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

#包括
#包括
int main(){
int i;
char-ch,buf[256];
文件*fp=fopen(“test.csv”,“r”);
对于(;;){
对于(i=0;i++){
ch=fgetc(fp);
如果(ch==','){
buf[i]='\0';
put(buf);
打破
}else if(ch='\n'){
buf[i]='\0';
put(buf);
fclose(fp);
返回0;
}else buf[i]=ch;
}
}
}
  • 这种方法是否尽可能有效和正确
  • 使用此方法测试EOF和文件读取错误的最佳方法是什么?(可能性:针对字符宏
    EOF
    feof()
    ferror()
    等进行测试)
  • 是否可以使用C++文件I/O <强>执行相同的任务,不损失效率?< /LI>
    使用
    fgets
    一次读取一行。C++文件I/O基本上是包装代码,里面有一些编译器优化(以及许多不需要的功能)。除非您正在阅读数百万行代码并测量时间,否则这并不重要

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

    话虽如此,您可以尝试以下几点:

    • 使用或本地操作系统(Windows有,可能还有其他)。然后不进行显式文件读取:只需像访问内存中的文件一样访问文件,不存在的任何内容都会被页面错误机制出错
    • 手动将整个文件读入缓冲区,然后处理该缓冲区。调用文件读取函数的次数越少,函数调用开销就越少,应用程序/OS域交换机也可能越少。显然,这会占用更多内存,但可能非常值得
    • 为您的问题和平台使用更优化的字符串扫描程序。一个角色一个角色地去做几乎从来没有像依赖你的问题领域附近的现有事物那样快。例如,您可以打赌
      strchr
      memchr
      可能比您可以自己滚动的大多数代码优化得更好,可以一次读取整个缓存线或单词,使用更好的算法扫描此类搜索,等等。对于更复杂的情况,你可以考虑一个完整的正则表达式引擎,它可以编译你的正则表达式到复杂的情况下。
    • 避免复制字符串。从“查找分隔符”和“分隔符之间的输出”的角度考虑可能会有所帮助。例如,您可以使用
      strchr
      查找下一个感兴趣的字符,然后使用
      fwrite
      或直接从输入缓冲区写入stdout的内容。然后将大部分工作保存在几个本地寄存器中,而不是使用堆栈或堆
      buf
    当你有疑问的时候,尝试一些可能性,然后描述,描述,描述

    同样对于这类问题,要非常清楚由操作系统和硬件缓存引起的运行之间的差异:在每次更改后分析一组运行,而不仅仅是一个运行——如果可能,使用可能总是命中缓存的测试(如果您试图测量最佳情况性能)或可能错过的测试(如果您试图衡量最坏情况下的绩效)


    关于C++文件艾奥(<代码> fStase<代码>等),请注意它们是更大、更复杂的兽类。它们倾向于包括诸如区域管理、自动缓冲等之类的东西,并且不太容易出现特定类型的编码错误。

    如果你做的事情相当简单(就像你在这里描述的),我倾向于发现C++库的东西阻碍了你的使用。(使用String String方法和一些C字符串函数在某个时候使用调试器和“STEP指令”,你会很快得到一个好的感觉。) 这完全取决于你将来是否想要或需要额外的功能或安全性


    最后,强制性的“不要为小事操心”。只有在非常重要的情况下,才在这里花时间进行优化。否则,请相信库和操作系统在大多数情况下都会为您做正确的事情——如果您在微观优化方面走得太远,您以后会发现您正在自食其果。这并不是为了阻止您从“我是否应该提前阅读整个文件,这会破坏未来的用例”——因为这是宏观的,而不是微观的


    但一般来说,如果你不是出于一个很好的理由而进行这种“让它更快”的调查——即“既然我已经编写了这个应用程序,那么需要它更好地执行,并且这个代码在分析器中显示得很慢”,或者“为了好玩,所以我可以更好地理解这个系统”——那么,首先把你的时间花在其他地方

    如果您要连续扫描文件,一种方法是使用2个大小足够的缓冲区(16K是SSD的最佳大小,4K是HDD IIRC的最佳大小。但16K应该可以同时满足这两种要求)。您可以从执行异步加载开始(在windows查找和Unix/OSX使用中)将第一个16K的字节加载到缓冲区0中,然后开始另一个加载到缓冲区1中的字节16K到32K。当读取位置达到16K时,交换缓冲区(因此您现在从缓冲区1读取),等待进一步加载到com
    #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;
            }
        }
    }