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

时间:2013-07-26 08:54:01

标签: c++ file

我看过很多帖子,但没找到像我想要的东西 我的输出错误了:

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

进入无限循环。

我的算法:

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

    #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;
    }
    

    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. 我正在练习提高我的基本知识,所以请不要发布任何函数(如tail -5 tc。)

    4. 请帮助改进我的代码。

8 个答案:

答案 0 :(得分:8)

代码中的评论

#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;
}

答案 1 :(得分:7)

您的代码存在许多问题。最多 重要的是你永远不会检查任何功能 成功了。并且ftellint的结果保存为pos < begin 一个非常好的主意。然后是测试fgetc; 这只有在出现错误时才会发生。而事实是 您将char的结果放在ftell中(结果如此) 在丢失信息)。而第一个读你的事实 do是在文件的末尾,因此将失败(并且一旦流进入 一个错误状态,它停留在那里)。事实上,你不能 可靠地对'ÿ'返回的值进行算术运算(除了 在Unix下)如果文件是在文本模式下打开的。

哦,没有“EOF角色”; fgetc是完全有效的 字符(Latin-1中的0xFF)。一旦分配了返回值 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 ); }

编辑:

提供这个想法的代码很快:

'\r'

这在错误处理方面有点弱;特别是你 可能想要区分无法打开 一个文件和任何其他错误。 (没有其他错误应该发生, 但你永远不知道。)

另外,这纯粹是Windows,它假设是实际的 文件包含纯文本,并且不包含任何{{1}} 不属于CRLF。 (对于Unix,只需删除旁边的 最后一行。)

答案 2 :(得分:2)

这可以非常有效地使用圆形阵列来完成。 不需要额外的缓冲区。

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
}

}

请提供反馈

答案 3 :(得分: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)。这是一种很好的做法。我不知道这段代码是否适用于空文件或类似的文件。

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:0)

使用: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);
}

答案 6 :(得分:0)

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

    int bitsPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.NV21);
    int bytePerPixel = bitsPerPixel >>> 3 + 1;
    int sizeInPixel = previewSize.getHeight() * previewSize.getWidth();
    int bufferSize = sizeInPixel * bytePerPixel;

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

#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;
}

答案 7 :(得分:0)

这是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>