从c / c ++中的文件读取最后n行

我看过很多post,但没找到像我想要的东西。
我输错了:

ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ...... // may be this is EOF character 

进入无限循环。

我的算法:

  1. 转到文件末尾。
  2. 将指针的位置减1并逐个字符地读取。
  3. 退出,如果我们找到我们的10行或我们到达文件的开头。
  4. 现在我将扫描整个文件直到EOF并打印它们//未在代码中实现。

码:

 #include #include #include #include #include using namespace std; int main() { FILE *f1=fopen("input.txt","r"); FILE *f2=fopen("output.txt","w"); int i,j,pos; int count=0; char ch; int begin=ftell(f1); // GO TO END OF FILE fseek(f1,0,SEEK_END); int end = ftell(f1); pos=ftell(f1); while(count<10) { pos=ftell(f1); // FILE IS LESS THAN 10 LINES if(pos<begin) break; ch=fgetc(f1); if(ch=='\n') count++; fputc(ch,f2); fseek(f1,pos-1,end); } return 0; } 

UPD 1:

改变了代码:它现在只有1个错误 – 如果输入有像这样的行

 3enil 2enil 1enil it prints 10 lines only line1 line2 line3ÿine1 line2 line3ÿine1 line2 line3ÿine1 line2 line3ÿine1 line2 

PS:
1.在记事本++中使用Windows工作

  1. 这不是功课

  2. 我也想在不使用任何内存或使用STL的情况下这样做。

  3. 我正在练习提高我的基本知识,所以请不要发布任何function(如tail -5 tc。)

请帮助改进我的代码。

您的代码存在许多问题。 最重要的一点是,您永远不会检查任何function是否成功。 并且将结果保存在int中也不是一个好主意。 然后是测试pos < begin ; 只有在出现错误时才会发生这种情况。 事实上,你将fgetc的结果放在一个char (导致信息丢失)。 事实上你做的第一次读取是在文件的末尾,所以会失败(一旦流进入错误状态,它就会停留在那里)。 如果在文本模式下打开文件,那么你无法对ftell返回的值(Unix下除外)进行可靠的算术运算。

哦,没有“EOF角色”; 'ÿ'是一个完全有效的字符(Latin-1中为0xFF)。 将fgetc的返回值分配给char ,您就失去了测试文件结尾的可能性。

我可能会补充说,一次向后读一个字符是非常低效的。 通常的解决方案是分配一个足够大的缓冲区,然后计算其中的'\n'

编辑:

只需一小段代码就可以提出这个想法:

 std::string getLastLines( std::string const& filename, int lineCount ) { size_t const granularity = 100 * lineCount; std::ifstream source( filename.c_str(), std::ios_base::binary ); source.seekg( 0, std::ios_base::end ); size_t size = static_cast( source.tellg() ); std::vector buffer; int newlineCount = 0; while ( source && buffer.size() != size && newlineCount < lineCount ) { buffer.resize( std::min( buffer.size() + granularity, size ) ); source.seekg( -static_cast( buffer.size() ), std::ios_base::end ); source.read( buffer.data(), buffer.size() ); newlineCount = std::count( buffer.begin(), buffer.end(), '\n'); } std::vector::iterator start = buffer.begin(); while ( newlineCount > lineCount ) { start = std::find( start, buffer.end(), '\n' ) + 1; -- newlineCount; } std::vector::iterator end = remove( start, buffer.end(), '\r' ); return std::string( start, end ); } 

这在error handling方面有点弱; 特别是,您可能希望区分无法打开文件和任何其他错误。 (不应该发生其他错误,但你永远不会知道。)

此外,这纯粹是Windows,它假设实际文件包含纯文本,并且不包含任何不属于CRLF的'\r' 。 (对于Unix,只需将最后一行放在旁边。)

代码中的注释

 #include  #include  int main(void) { FILE *in, *out; int count = 0; long int pos; char s[100]; in = fopen("input.txt", "r"); /* always check return of fopen */ if (in == NULL) { perror("fopen"); exit(EXIT_FAILURE); } out = fopen("output.txt", "w"); if (out == NULL) { perror("fopen"); exit(EXIT_FAILURE); } fseek(in, 0, SEEK_END); pos = ftell(in); /* Don't write each char on output.txt, just search for '\n' */ while (pos) { fseek(in, --pos, SEEK_SET); /* seek from begin */ if (fgetc(in) == '\n') { if (count++ == 10) break; } } /* 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); return 0; } 

这可以非常有效地使用圆形arrays来完成。 无需额外的缓冲区。

 void printlast_n_lines(char* filename, int n){ const int k =n; ifstream file(fileName); string l[k]; int size = 0 ; while(file.good()){ getline(file, l[size%k]); //this is just circular array size++; } //start of circular array & size of it int start = size > k ? (size%k) : 0 ; //this get the start of last k lines int count = min(k,size); // no of lines to print for(int i = 0; i< count ; i++){ cout << l[(start+i)%k] << endl ; // start from inbetween and print from start due to remainder till all counts are covered } } 

请提供反馈

 int end = ftell(f1); pos=ftell(f1); 

这告诉你文件的最后一点,所以EOF。 当你阅读时,你得到EOF错误,并且ppointer想要向前移动1个空格……

所以,我建议将当前位置减少一个。 或者将fseek(f1,-2,SEEK_CUR)放在while循环的开头,以便将fread补偿1点并返回1点……

我相信,你正在使用fseek错误。 在Google上查看man fseek

试试这个:

 fseek(f1, -2, SEEK_CUR); //1 to neutrialize change from fgect //and 1 to move backward 

您还应该在开头设置位置到最后一个元素:

 fseek(f1, -1, SEEK_END). 

您不需要end变量。

您应该检查所有函数的返回值( fgetcfseekftell )。 这是一个很好的做法。 我不知道这段代码是否适用于空文件或类似的东西。

使用: fseek(f1,-2,SEEK_CUR); 回来

我写这段代码,它可以工作,你可以尝试:

 #include "stdio.h" int main() { int count = 0; char * fileName = "count.c"; char * outFileName = "out11.txt"; FILE * fpIn; FILE * fpOut; if((fpIn = fopen(fileName,"r")) == NULL ) printf(" file %s open error\n",fileName); if((fpOut = fopen(outFileName,"w")) == NULL ) printf(" file %s open error\n",outFileName); fseek(fpIn,0,SEEK_END); while(count < 10) { fseek(fpIn,-2,SEEK_CUR); if(ftell(fpIn)<0L) break; char now = fgetc(fpIn); printf("%c",now); fputc(now,fpOut); if(now == '\n') ++count; } fclose(fpIn); fclose(fpOut); } 

我将使用两个流来打印文件的最后n行:它在O(行)运行时O(行)空间中运行

 #include using namespace std; int main(){ // read last n lines of a file ifstream f("file.in"); ifstream g("file.in"); // move f stream n lines down. int n; cin >> n; string line; for(int i=0; i 

具有O(行)运行时O(N)空间的解决方案正在使用队列:

 ifstream fin("file.in"); int k; cin >> k; queue Q; string line; for(; getline(fin, line); ){ if(Q.size() == k){ Q.pop(); } Q.push(line); } while(!Q.empty()){ cout << Q.front() << endl; Q.pop(); }