C语言读取不含换行符行的方法及fgets分词变体相关问询
fgets 去换行与按行读取token相关问题解答
核心问题解答
- 标准C库没有原生提供自动去除末尾换行符的
fgets变体,常规的低成本实现方案是调用fgets读取到字符串后,手动判断末尾是否为\n,如果是就替换为\0即可。如果需要封装成独立函数,你实现的fgetsnn逻辑是可行的。 - 标准库也没有自带直接读取一行内所有token的
fgets版本,你可以先读取到不带换行的整行字符串后,搭配strtok/strtok_r系列函数拆分token即可满足需求。
性能问题说明
你观察到自己实现的逐字符读取版本比原生fgets慢10%~20%是正常现象。原生fgets内部使用块读取机制批量填充文件缓冲区,不需要每次都调用fgetc触发单次字符读取的逻辑开销,性能自然更高。
你提到的switch分支顺序影响性能的判断是对的,出现概率最高的普通字符分支排在最后会增加多余的判断开销,你后续调整的把普通字符用if优先判断的优化思路是合理的,能够减少分支预测失败的概率,提升运行效率。
至于你尝试一次性读取整个文件没有看到性能提升,这是因为现代操作系统默认就会对文件做页缓存,读取文件时内核已经提前把文件内容缓存到内存中了,用户态自己实现全量读取的增益会被系统缓存抵消。如果是机械硬盘的场景,大部分时候性能瓶颈都在硬件IO上,用户态逻辑优化带来的性能提升不会特别明显。
你提到的修改ASCII码顺序提升拆分速度的思路,在通用场景下不具备可操作性,因为ASCII码是标准定义的,无法随意修改,如果要提升token拆分速度,可以在整行读取到内存后,再一次性遍历拆分,避免IO和拆分逻辑耦合,能获得更好的性能。
参考实现示例
逐字符读取的switch版本
// Read characters from file and exclude terminating newline // Input argument 'size' must be integer larger than one char *fgetsnn(char *s0, int size, FILE *fp) { size--; char c; char *s=s0; char *se=s0+size; while (s<se) { c=fgetc(fp); switch (c) { case EOF: *s='\0'; if (ferror(fp)!=0) { return(NULL); }; if (s==s0) { return(NULL); } else { return(s0); }; case '\n': *s='\0'; return(s0); case '\0': *s='\0'; return(NULL); default: *s=c; s++; }; }; *s='\0'; return(s0); };
优化分支判断的if+switch版本
// Read characters from file and exclude terminating newline // Input argument 'size' must be integer larger than one char *fgetsnn(char *s0, int size, FILE *fp) { size--; char c; char *s=s0; char *se=s0+size; while (s<se) { c=fgetc(fp); if (c>'\n') { *s=c; s++; } else { switch (c) { case '\n': *s='\0'; return(s0); case EOF: *s='\0'; if (ferror(fp)!=0) { return(NULL); }; if (s==s0) { return(NULL); } else { return(s0); }; case '\0': *s='\0'; return(NULL); default: *s=c; s++; }; }; }; *s='\0'; return(s0); };
内容的提问来源于stack exchange,提问作者user1070696
相关产品推荐
相关产品推荐

