【问题标题】:How to read a growing text file in C++?如何在 C++ 中读取不断增长的文本文件?
【发布时间】:2012-07-30 05:37:38
【问题描述】:

我正在尝试从一个正在增长的文件中读取数据(类似于 tail -F 所做的),但我的代码肯定有一些问题:

string   log, logFile("test.log");
size_t   p = 0;

while(true)
{
    ifstream ifs(logFile.c_str());

    ifs.seekg(p);  //*1

    while(ifs.eof() == false)
    {
        getline(ifs, log);

        cout << log << endl;

        p = ifs.tellg();  //*2
    }

    nanosleep(&pause, NULL);
}

如果没有 //*1 和 //*2 行,日志文件会被正确读取到其末尾,但如果添加新行,则不会发生任何事情。

使用 seekg 和 tellg 我正在尝试存储文件的当前结束位置,这样当我重新打开它时,我可以去那里阅读已添加的内容。

我想知道我的代码出了什么问题,以及是否真的有必要为此关闭并重新打开同一个文件。

谢谢。

【问题讨论】:

    标签: c++ logging stl fstream seekg


    【解决方案1】:

    循环不正确,因为遇到eof()tellg() 返回-1,并且在调用getline() 后没有立即检查eof(),这是需要的。将循环更改为:

    while (getline(ifs, log))
    {
        cout << log << endl;
        p = ifs.tellg();
    }
    

    此外,当tellg() 返回-1 时,p 被声明为size_tp 的值被设置为4294967295。这意味着 seekg() 被设置为超出文件末尾。将p的类型改为std::streamoff,确认调用seekg()成功:

    if (ifs.seekg(p))
    {
        while (getline(ifs, log))
        {
            cout << log << endl;
            p = ifs.tellg();
        }
    }
    

    如果确实有必要为此关闭并重新打开同一个文件。

    不,这不是必需的,但您需要 clear() 来自流的 eof 状态。以下是已发布代码的更正版本的替代方案:

    #include <iostream>
    #include <string>
    #include <fstream>
    
    int main()
    {
        std::ifstream ifs("test.log");
    
        if (ifs.is_open())
        {
            std::string line;
            while (true)
            {
                while (std::getline(ifs, line)) std::cout << line << "\n";
                if (!ifs.eof()) break; // Ensure end of read was EOF.
                ifs.clear();
    
                // You may want a sleep in here to avoid
                // being a CPU hog.
            }
        }
    
        return 0;
    }
    

    【讨论】:

    • 我注意到如果我将 ifstream 对象创建到循环中它可以工作,但如果我在外部创建它则不会。是否需要关闭并重新打开文件才能读取已添加的内容?
    • @Pietro,您需要在下一次读取尝试之前调用ifs.clear() 来清除ifstream 的eof 状态。我认为这种方法不需要tellg()seekg()
    • @hmjd,你就是那个男人!我不得不去地狱然后回来找到这个简单问题的简单答案。我还没有看到任何像这样清晰的东西。
    • @hmjd:我刚试了你的最后一段代码,当我在输入 test.log 文件中添加新行时,它们没有被选中。
    【解决方案2】:

    这种方法对我很有效:

    #include <string>
    #include <chrono>
    #include <thread>
    #include <fstream>
    #include <iostream>
    
    int main(int, char* argv[])
    {
        // open file passed in on command line (at end of file)
        std::ifstream ifs(argv[1], std::ios::ate);
    
        if(!ifs.is_open())
        {
            std::cerr << "ERROR: opening log file: " << argv[1] << '\n';
            return 1;
        }
    
        // remember file position
        std::ios::streampos gpos = ifs.tellg();
    
        std::string line;
        bool done = false;
    
        while(!done)
        {
            // try to read line
            if(!std::getline(ifs, line) || ifs.eof())
            {
                // if we fail, clear stream, return to beginning of line
                ifs.clear();
                ifs.seekg(gpos);
    
                // and wait to try again
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                continue;
            }
    
            // remember the position of the next line in case
            // the next read fails
            gpos = ifs.tellg();
    
            // process line here
            std::cout << "line: " << line << std::endl;
        }
    }
    

    【讨论】:

      【解决方案3】:

      此代码适用于我:

      struct timespec pause;
      pause.tv_sec  = 1;
      pause.tv_nsec = 0;
      
      std::ifstream ifs("test.log");
      std::streamoff p;
      
      if(ifs.is_open())
      {
          std::string line;
      
          while(true)
          {
              if(ifs.seekg(p))
              {
                  while(std::getline(ifs, line))
                  {
                      std::cout << line << std::endl;
                      p = ifs.tellg();
                  }
              }
      
              ifs.clear();
      
              nanosleep(&pause, NULL);
          }
      }
      

      【讨论】:

        【解决方案4】:

        由于这些答案都不起作用,我想出了一个有效的答案......

        #include <iostream>
        #include <string>
        #include <fstream>
        
        using namespace std;
        
        int main()
        {
            string   log, logFile("test.txt");
            std::streamoff   p = 0;
            ifstream ifs(logFile.c_str());
        
            while(true)
            {
        
                ifs.seekg(p);  //*1
                while (getline(ifs, log))
                {
                    cout << log << endl;
                    if(ifs.tellg() == -1) p = p + log.size();
                    else p = ifs.tellg();
                }
                ifs.clear();
        
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-11
          相关资源
          最近更新 更多