c++如何判断文件末尾

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++如何判断文件末尾相关的知识,希望对你有一定的参考价值。

参考技术A

使用标准库函数feof()可判断是否到达文件结尾。

用法: 

#include <stdio.h>

int feof(FILE *stream);

参数流 :FILE结构的指针。

功能:feof是C语言标准库函数,其原型在stdio.h中,其功能是检测流上的文件结束符,如果文件结束,则返回非0值,否则返回0(即,文件结束:返回非0值,文件未结束,返回0值),文件结束符只能被clearerr()清除。

扩展资料:

注意事项:

1、feof判断文件结束是通过读取函数fread/fscanf等返回错误来识别的,故而判断文件是否结束应该是在读取函数之后进行判断。比如,在while循环读取一个文件时,如果是在读取函数之前进行判断,则如果文件最后一行是空白行,可能会造成内存错误。

2、feof()是先读文件在判断是不是到达文件结束,所以当使用feof()作为循环判断条件的话,就需要注意一种情况,就是当文件为空的时候,它也会读取一次文件内容然后再跳出循环,所以有可能会读取到一些不可知的内容出来。

参考资料来源:百度百科-feof

如何使用 C++ 中的流从文件末尾读取给定数量的行?

【中文标题】如何使用 C++ 中的流从文件末尾读取给定数量的行?【英文标题】:How do I read a given number of lines from the end of the file using streams in C++? 【发布时间】:2011-01-06 03:36:14 【问题描述】:

为了在 Linux 中实现 tail shell 命令,我需要使用流输入/输出从文件末尾读取一定数量的行/字节。有没有人有建议如何做到这一点?我怀疑我需要打开一个文件并将一些参数传递给 ifstream 构造函数,但我不知道究竟是什么。谷歌搜索没有找到任何东西。

【问题讨论】:

标准库与此无关——您将不得不编写一些代码。而要实现tail的-f标志,你将不得不使用一些非标准的东西。 流不是为此而设计的。流用于序列化(文本数据的反序列化)。像代码一样下拉到 C 会更容易。 【参考方案1】:

这显示了您如何在 C++ 中执行此操作...从文件末尾读取连续的块,然后扫描这些块以查找新行。如果没有找到换行符,则必须保留部分块并与读取的下一个块结合...

//
// USAGE: lastln COUNT [FILE]
//
// Print at most COUNT lines from the end of FILE or standard input.
// If COUNT is -1, all lines are printed.
//

#include <errno.h>
#include <libgen.h>
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main(int argc, char **argv)

  int ret = 0, maxLines = -1, len, count = 0, sz = 4096, lines = 0, rd;
  istream *is;
  ifstream ifs;
  stringstream ss;
  char *buf = NULL;
  const char *prog = (argc > 0 && argv[0] ? basename(argv[0]) : "");
  string line;

  if (argc > 1) 
    if ((maxLines = atoi(argv[1])) == 0) 
      goto end;
    
  

  if (argc > 2 && !(argv[2] && argv[2][0] == '-' && argv[2][1] == '\0')) 
    ifs.open(argv[2], ios::in | ios::binary);
    if (!ifs) 
      ret = 1;
      cerr << prog << ": " << argv[2] << ": " << strerror(errno) << endl;
      goto end;
    
    is = &ifs;
   else 
    ss << cin.rdbuf();
    if (!ss) 
      ret = 1;
      cerr << prog << ": failed to read input" << endl;
      goto end;
    
    is = &ss;
  

  is->seekg(0, ios::end);
  len = is->tellg();
  buf = new char[sz + 1];

  while (rd = min(len - count, sz)) 
    is->seekg(0 - count - rd, ios::end);
    is->read(buf, rd);
    count += rd;
    char *p = buf + rd, *q;
    *p = '\0';

    for (;;) 
      q = (char *)memrchr(buf, '\n', p - buf);
      if (q || count == len) 
        if (q) *q = '\0';
        if (lines || p - q - 1 > 0 || !q) 
          ++lines;
          cout << lines << ": " << (q ? q + 1 : buf) << line << endl;
          line.clear();
          if (lines >= maxLines && maxLines != -1) break;
        
        if (q) p = q; else break;
       else 
        line = string(buf, p - buf) + line;
        break;
      
    
  

  end:

  if (buf) delete[] buf;
  return ret;

【讨论】:

这个程序无缘无故地使用指针和手动内存管理。该程序也可以将 std::vector 用于动态分配的数据和自动存储 std::ifstream 而不是指针和动态分配。这避免了最后的所有清理,从而也避免了使用 goto。它还避免了内存泄漏。 不,您需要一个指针memrchr。它只需要一个指针。向量将是一个毫无意义的间接层。一个指针是必要的在这里使用cin。您似乎忘记了 references can't be reseated in c++。 goto 是必要的,因此 OP 可以在运行结束时打印消息。并且不需要在 exit() 之前释放数据。无论如何,你都沉迷于语法糖。 我称之为“更喜欢可读代码”,而不是“沉迷于语法糖”。这段代码读起来更像 C 而不是惯用的 C++。您可以通过调用front() 并获取地址来获取指向std::vector 内容的指针,以便与memrchr 一起使用。使用cin不需要指针;您可以在命令行上同时支持标准输入和文件名,而无需使用指针,只需将代码放在一个接受istream&amp; 参数的函数中,并根据需要传递std::cinstd::ifstream 实例。 @Wyzard:您的解决方案需要添加一个不必要的函数多次调用它来处理指针旨在解决的问题c++ 的内置多态性。您的解决方案无法扩展。如果有超过 2 种流类型怎么办?您创建冗余分支。您将非常长以避免指针,这对语法糖很着迷。你承认即使使用向量,底层指针无论如何都会暴露出来,所以你仍在使用指针【参考方案2】:
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main()

  ifstream is("file.txt", ios::binary);
  if (!is) 
    cout << "Failed to open file" << endl;
    return 1;
  

  is.seekg(0, ios::end);
  int len = is.tellg();
  char c;
  int n = 0;
  ostringstream line;
  int lines = 0;

  for (int i = len - 1; i >= 0; --i) 
    is.seekg(i, ios::beg);
    is.get(c);
    if (c == '\n' || i == 0) 
      if (i < len - 1) 
        if (i == 0) 
          line << c;
        
        string s = line.str();
        cout << lines << ": " << string(s.rend() - n, s.rend()) << endl;
        ++lines;
        n = 0;
        line.seekp(0, ios::beg);
      
     else 
      line << c;
      ++n;
    
  

  is.close();

  return 0;

【讨论】:

从文件开头向后搜索(即文件大小小于 4096 字节)实际上是未定义的【参考方案3】:

由于 tail 需要与管道一起使用,因此您无法倒带,因此您必须保留您已阅读的最后 n 行的旋转缓冲区,您将在 EOF 上转储。

【讨论】:

此方法适用于短文件。但是大文件需要不同的技术。您需要寻找到最后,然后开始备份。 您可以对不同的文件使用不同的技术,但我描述的技术对于标准输入等仍然是必需的。 而且,功课不需要表现的很好,至少他没说。【参考方案4】:

这个问题类似于获取单链表的最后一个n 节点的问题。您必须使用n 行的缓冲区一直走到最后,然后从缓冲区中吐出这些行。

【讨论】:

【参考方案5】:

我不认为有一个简单的方法可以解决这个问题,您可能需要查找文件末尾,备份一个“块”(任意大小,但可能是几千字节),读取该“块”数据并开始在其中查找换行符,如果找不到足够的数据,则备份两倍的块大小(请记住,您向前阅读,因此您需要备份您阅读的那个,加上你接下来要读的那篇),然后再读另一篇。

HTH

【讨论】:

以上是关于c++如何判断文件末尾的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中使用 ReadBlock 方法时,如何判断何时到达文件末尾?

fread()函数如何判断是不是到文件末尾?

如何利用c++ 把数据加入到txt文件的末尾而不覆盖文件中已有的数据

C++ 如何判断路径是文件还是目录

C++怎样将一个数插入到数组末尾?

如何使用 C++ 写入文件的特定列?