我想一次一个地从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;
}
}
}
EOF
,feof()
,ferror()
等进行测试。 答案 0 :(得分:5)
最有效的方法很大程度上取决于操作系统,标准库(例如libc
),甚至是您运行的硬件。这使得几乎不可能告诉你什么是“最有效的”。
有人说过,你可以尝试一些事情:
mmap()
或等效的本地操作系统(Windows有CreateFileMapping / OpenFileMapping / MapViewOfFile,可能还有其他操作系统)。然后你不进行显式的文件读取:你只需要访问该文件就好像它已经在内存中一样,并且任何不存在的文件都会被页面错误机制搞砸。strchr
和memchr
可能比您可以自己动手的大多数代码更优化,执行诸如一次读取整个缓存行或单词之类的操作,使用更好的算法扫描这种类型的对于更复杂的情况,你可能会考虑一个完整的正则表达式引擎,它可以将你的正则表达式快速编译成复杂的情况。strchr
查找下一个感兴趣的字符,然后使用fwrite
或其他东西直接从输入缓冲区写入stdout。然后,您将大部分工作保留在几个本地寄存器中,而不是使用堆栈或堆buf
。但是,如果有疑问,请尝试一些可能性和个人资料,个人资料,个人资料。
同样对于这类问题,要非常了解由操作系统和硬件缓存引起的运行之间的差异:在每次更改后分析一堆运行而不是仅运行一次 - 如果可能的话,使用可能的测试总是点击缓存(如果你试图测量最佳情况的性能)或可能会错过的测试(如果你试图测量最坏情况的性能)。
关于C ++文件IO(fstream
等),请注意它们是更大,更复杂的野兽。它们往往包括诸如区域设置管理,自动缓冲等之类的东西 - 以及不太容易出现特定类型的编码错误。
如果你正在做一些非常简单的事情(就像你在这里描述的那样),我倾向于发现C ++库的东西会受到妨碍。 (通过字符串流方法使用调试器和“步骤指令”,而不是某些C字符串函数,你很快就会对此感觉良好。)
这完全取决于您将来是否想要或需要额外的功能或安全性。
最后,强制性的“不要让小东西流汗”。如果真的很重要的话,只花时间在这里进行优化。否则,请相信图书馆和操作系统会在大多数时间为您做正确的事情 - 如果您对微观优化过于苛刻,您会发现自己在以后拍摄自己。这并不是为了阻止你思考“我应该提前阅读整个文件,是否会破坏未来的用例” - 因为这是宏观,而不是微观。
但总的来说,如果你没有做出这种“快速进行”调查的原因有充分的理由 - 即“现在我已经写好了,需要这个应用程序才能表现得更好”,这段代码表现得很慢在探查器“,或”这样做是为了好玩,所以我可以更好地理解系统“ - 好吧,先把时间花在别的地方。 =)
答案 1 :(得分:2)
如果要连续扫描文件,一种方法是使用2个足够大的缓冲区(16K是SSD的最佳大小,HDD是硬盘IIRC的4K。但16K应该足够了)。首先执行异步加载(在Windows中查找Overlapped I/O并在Unix / OSX上使用O_NONBLOCK)将第一个16K放入缓冲区0,然后再启动另一个加载到缓冲区1中的16K到32K字节。当您的读取位置达到16K时,交换缓冲区(因此您现在正在从缓冲区1读取)等待任何进一步的加载完成到缓冲区1中并执行32K到48K字节的异步加载到缓冲区0中,依此类推。通过这种方式,您在处理前一个16K时,不必等待负载完成的可能性要小得多。
我在我的XML解析器中转移到这样的方案之前一直在使用fopen和fgetc,并且加速很大。加载15兆字节的XML文件并将其处理从几分钟缩短到几秒钟。当然,你的milage可能会有所不同。
答案 2 :(得分:0)
使用fgets
一次读取一行。 C ++文件I / O基本上是包装代码,其中包含一些编译器优化(以及许多不需要的功能)。除非您正在阅读数百万行代码并测量时间,否则无关紧要。