使用C++打印文件最后N行时遭遇fseek异常问题求助
问题:读取文件最后N行时首行首字符丢失的原因分析
代码实现
用户编写的C++代码用于提取输入文件的最后N行并写入输出文件:
#include <iostream> #include <fstream> #include <string> using namespace std; void printLastNLines(const std::string& inputFileName, const std::string& outputFileName, int N); int main() { printLastNLines("test.csv", "test2.csv", 200); } void printLastNLines(const std::string& inputFileName, const std::string& outputFileName, int N) { FILE* in, * out; int count = 0; long int pos; char s[100]; fopen_s(&in, inputFileName.c_str(), "rb"); /* always check return of fopen */ if (in == NULL) { perror("fopen"); exit(EXIT_FAILURE); } fopen_s(&out, outputFileName.c_str(), "wb"); if (out == NULL) { perror("fopen"); exit(EXIT_FAILURE); } fseek(in, 0, SEEK_END); pos = ftell(in); while (pos) { pos--; fseek(in, pos, SEEK_SET); char c = fgetc(in); if (c == '\n') { if (count++ == N) break; } } //fseek(in, pos, SEEK_SET); /* Write line by line, is faster than fputc for each char */ while (fgets(s, sizeof(s), in) != NULL) { fprintf(out, "%s", s); } fclose(in); fclose(out); }
测试情况
- 测试文件
test.csv内容:
2 3
- 运行代码后,输出文件
test2.csv仅包含:
3
- 更大文件测试时,提取的首行第一个字符总是丢失;取消注释代码中的
fseek(in, pos, SEEK_SET);后问题解决。
原因分析
问题的核心是**fgetc会自动推进文件指针**,导致循环结束后文件指针的位置与你预期的pos不一致:
- 循环中每次执行
fseek(in, pos, SEEK_SET);后,文件指针定位到pos位置; - 调用
fgetc(in)读取该位置的字符时,文件指针会自动向后移动1个字节,指向pos+1的位置; - 循环结束后,你误以为文件指针停留在
pos位置,但实际上它已经在pos+1处;此时直接调用fgets,会从pos+1开始读取,自然跳过了pos位置的字符——也就是你丢失的首行第一个字符。
以你的测试文件为例:
文件字节布局为'2' '\n' '3' '\n',总长度4。循环结束时pos=0,但经过最后一次fgetc读取'2'后,文件指针已经移动到1的位置。此时fgets从1开始读取,拿到的内容是"\n3\n",写入后就只剩"3\n"。
取消注释fseek(in, pos, SEEK_SET);后,会将文件指针重新定位到pos位置,确保fgets从正确的起始点开始读取,从而避免丢失字符。
内容的提问来源于stack exchange,提问作者ubaabd
相关产品推荐
相关产品推荐

