[英]reading last n lines from file in c/c++
我看過很多帖子,但沒有找到我想要的。
我得到錯誤的輸出:
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ...... // may be this is EOF character
進入無限循環。
我的算法:
代碼:
#include<iostream>
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
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;
}
更新 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上工作
這不是家庭作業
我也想在不使用更多內存或使用 STL 的情況下做到這一點。
我正在練習以提高我的基本知識,所以請不要發布任何功能(例如 tail -5 tc。)
請幫助改進我的代碼。
代碼中的注釋
#include <stdio.h>
#include <stdlib.h>
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;
}
您的代碼存在許多問題。 最重要的是,您永遠不會檢查任何功能是否成功。 將結果以ftell
保存在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<size_t>( source.tellg() );
std::vector<char> buffer;
int newlineCount = 0;
while ( source
&& buffer.size() != size
&& newlineCount < lineCount ) {
buffer.resize( std::min( buffer.size() + granularity, size ) );
source.seekg( -static_cast<std::streamoff>( buffer.size() ),
std::ios_base::end );
source.read( buffer.data(), buffer.size() );
newlineCount = std::count( buffer.begin(), buffer.end(), '\n');
}
std::vector<char>::iterator start = buffer.begin();
while ( newlineCount > lineCount ) {
start = std::find( start, buffer.end(), '\n' ) + 1;
-- newlineCount;
}
std::vector<char>::iterator end = remove( start, buffer.end(), '\r' );
return std::string( start, end );
}
這在錯誤處理方面有點弱; 特別是,您可能想要區分無法打開文件和任何其他錯誤。 (不應該發生其他錯誤,但您永遠不知道。)
此外,這純粹是 Windows,它假設實際文件包含純文本,並且不包含任何不屬於 CRLF 的'\\r'
。 (對於 Unix,只需刪除最后一行的下一行。)
這可以非常有效地使用圓形陣列來完成。 不需要額外的緩沖區。
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
cout << l[size%k] << '\n';
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] << '\n' ; // start from in between and print from start due to remainder till all counts are covered
}
}
請提供反饋。
我相信,你使用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
變量。
您應該檢查所有函數( fgetc
、 fseek
和ftell
)的返回值。 這是很好的做法。 我不知道這段代碼是否適用於空文件或類似的東西。
int end = ftell(f1);
pos=ftell(f1);
這告訴你文件的最后一個點,所以 EOF。 當你閱讀時,你得到了 EOF 錯誤,指針想要向前移動 1 個空格......
因此,我建議將當前位置減一。 或者將 fseek(f1, -2,SEEK_CUR) 放在 while 循環的開頭,以彌補 fread 1 點並返回 1 點......
使用: 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(lines) 運行時和O(lines) 空間中運行。
#include<bits/stdc++.h>
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<k; ++i) getline(f,line);
// move f and g stream at the same pace.
for(; getline(f,line); ){
getline(g, line);
}
// g now has to go the last n lines.
for(; getline(g,line); )
cout << line << endl;
}
具有O(lines) 運行時和O(N) 空間的解決方案是使用隊列:
ifstream fin("file.in");
int k;
cin >> k;
queue<string> 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();
}
這是 C++ 中的解決方案。
#include <iostream>
#include <string>
#include <exception>
#include <cstdlib>
int main(int argc, char *argv[])
{
auto& file = std::cin;
int n = 5;
if (argc > 1) {
try {
n = std::stoi(argv[1]);
} catch (std::exception& e) {
std::cout << "Error: argument must be an int" << std::endl;
std::exit(EXIT_FAILURE);
}
}
file.seekg(0, file.end);
n = n + 1; // Add one so the loop stops at the newline above
while (file.tellg() != 0 && n) {
file.seekg(-1, file.cur);
if (file.peek() == '\n')
n--;
}
if (file.peek() == '\n') // If we stop in the middle we will be at a newline
file.seekg(1, file.cur);
std::string line;
while (std::getline(file, line))
std::cout << line << std::endl;
std::exit(EXIT_SUCCESS);
}
建造:
$ g++ <SOURCE_NAME> -o last_n_lines
運行:
$ ./last_n_lines 10 < <SOME_FILE>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.