程序应该显示文件的最后 5 行,但它不适用于大文件

Posted

技术标签:

【中文标题】程序应该显示文件的最后 5 行,但它不适用于大文件【英文标题】:the program should display the last 5 lines of the file, but it does not work with large files 【发布时间】:2019-11-13 10:29:49 【问题描述】:

我写了一个程序,应该打印文件的最后 5 行,但是老师创建了一个 4 GB 的文件,程序崩溃了。如何重写程序以便它可以处理非常大的文件

一种可能的解决方案是逐字符读取文件,但我不知道该怎么做

这是c++程序代码

#include <iostream>

#include <fstream>

#include <string>

using std::ifstream;
using std::cout;
using std::string;
using std::getline;

int main(int argc, char * argv[], char * env[]) 
  setlocale(LC_ALL, "");
  int i;
  string line;

  if (argc == 3) 

    string filename = argv[1];

    ifstream myfile(filename);
    string n = argv[2];

    int nn = atoi(n.c_str());

    string line, buffer[nn];
    const size_t size = sizeof buffer / sizeof * buffer;
    size_t i = 0;

    while (getline(myfile, line)) 
      buffer[i] = line;
      if (++i >= size) 
        i = 0;
      
    

    for (size_t j = 0; j < size; ++j) 
      cout << buffer[i] << "\n";
      if (++i >= size) 
        i = 0;
      
    
    //return 0;

  


【问题讨论】:

从描述它应该做什么开始(即使它很小,从写得不好的代码中弄清楚这一点也很不愉快)。看起来它有问题。 buffer[nn]; - 不要使用变长数组。 如果只显示最后 5 行,则不需要将所有文件内容存储在内存中。只存储最后读取的 5 行并在最后显示。 这是一些作业,不允许您使用某些库类还是可以使用 STL? 我没有看到任何明显的损坏,尽管它可能很丑。是时候停止盯着代码并启动实际的调试器了! 【参考方案1】:

问题一定是那个 4GB 文件中的大行。您的解决方案缓冲(然后删除)每一行,并且至少其中一行可能太长而无法在您正在运行的机器中进行缓冲,从而使您的程序崩溃。

您应该从末尾开始读取文件,计算换行符的数量,并在达到nn + 1 的计数时停止并输出其余部分。当您需要处理大行时,缓冲最后的 nn 行不是一个好选择。

这里有一个可以帮助您的解决方案:

array<char, 64 * 1024> buffer; // 64kb of buffer

size_t nn = atoi(n.c_str()); 

myfile.seekg(0, ios_base::end); 

unsigned int nlcount = 0; 
size_t length = myfile.tellg(); 
size_t oldpos = length; 

while (myfile.tellg() > 0)  
  size_t newpos = oldpos - min(oldpos, buffer.size()); 
  myfile.seekg(newpos); 
  size_t rdsize = oldpos - newpos; 
  myfile.read(buffer.data(), rdsize); 
  if (!myfile)  
    cerr << "failed while looking for newlines\n"; 
    return 1; 
   
  auto rit = buffer.rbegin() + (buffer.size() - rdsize); 
  while (rit != buffer.rend() && nlcount <= nn)  
    if (*rit == '\n')  
      ++nlcount; 
     
    ++rit; 
   
  if (nlcount > nn)  
    myfile.seekg(newpos + (buffer.rend() - rit) + 1); 
    break; 
   
  oldpos = newpos; 
 

如果nlcount 等于nn + 1,这会将输入流指向您只需要输出其余部分的确切位置。我建议您不要使用缓冲行输出它,而是使用固定大小的缓冲区:

while (myfile.peek() != EOF) 
  myfile.read(buffer.data(), buffer.size());
  cout.write(buffer.data(), myfile.gcount());

不要使用getline(),否则在处理长行时,您仍然会缓冲行并崩溃。

【讨论】:

所描述的问题与长线无关,所以使用getline()没有问题 因为它不在问题中。你应该回答这个问题而不是你的假设。 我不是说你错了,我只是说不要确定并根据你的假设提出建议。此外,您没有提供为什么您说getline() 会失败以及使用它的限制是什么。 “读取长行文件的问题”非常笼统,不应用作答案。【参考方案2】:

要消除缓冲区依赖性,一种方法是从末尾向后读取文件以达到所需的行数。 5 在这里是硬编码的,但您可以将其作为参数传递。

std::ifstream fileReader("test.txt", std::ios_base::ate );
std::string currentLine;
long length;
int lines;
char c = '\0';

if( fileReader )

    length = fileReader.tellg();
    for(long i = length-2; i > 0; i-- )
    
        fileReader.seekg(i);
        c = fileReader.get();
        if( c == '\r' || c == '\n' )
        
            lines++;
            if (lines == 5)
                break;
        
    

    while(fileReader)
    
        std::getline(fileReader, currentLine);
        std::cout << currentLine << std::endl;
    


【讨论】:

@FernandoSilveira 我已经在 4GB 文件上对其进行了测试,结果还可以。它也没有任何明确的缓冲数组。 @FernandoSilveira “4GB 文件”而不是“4GB 线路”。仔细阅读问题。 你假设太多了;) 我的一些 cmets 正在被移除(不是我自己),所以我会继续将它们全部移除。 如果输入包含不属于 crlf 的任何 '\r',则此方法不起作用。而且它也会分解成很长的行。

以上是关于程序应该显示文件的最后 5 行,但它不适用于大文件的主要内容,如果未能解决你的问题,请参考以下文章

来自画廊工作的文件选择器,但它不适用于 android webview 中的相机

零配置jQuery Datatable默认分页不适用于大表

Laravel img src 不适用于我的文件路径

程序不适用于 C 中的大文件

水平进度条不适用于 Asynctask Android 下载文件?

Chrome 调试器不适用于打字稿文件