【问题标题】:c++ fastest way to read only last line of text file?c++ 最快的方法来读取文本文件的最后一行?
【发布时间】:2012-08-06 06:11:27
【问题描述】:

我只想读取文本文件的最后一行(我在 UNIX 上,可以使用 Boost)。我知道的所有方法都需要扫描整个文件以获取最后一行,这根本没有效率。有没有一种只获取最后一行的有效方法?

另外,我需要它足够健壮,即使有问题的文本文件不断被另一个进程附加,它也能正常工作。

【问题讨论】:

  • 在某人不断修改文件这一事实中,是否有任何东西是可靠的?在这种情况下,您如何定义“稳健”?
  • @user788171 您应该能够搜索到最后并向后扫描行终止符。不过,我可能会建议你不要在这里使用原始文件,因为这听起来更像是你想要一个管道。

标签: c++ iostream seek


【解决方案1】:

使用 seekg 跳转到文件末尾,然后回读直到找到第一个换行符。 下面是一些使用 MSVC 的示例代码。

#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main()
{
    string filename = "test.txt";
    ifstream fin;
    fin.open(filename);
    if(fin.is_open()) {
        fin.seekg(-1,ios_base::end);                // go to one spot before the EOF

        bool keepLooping = true;
        while(keepLooping) {
            char ch;
            fin.get(ch);                            // Get current byte's data

            if((int)fin.tellg() <= 1) {             // If the data was at or before the 0th byte
                fin.seekg(0);                       // The first line is the last line
                keepLooping = false;                // So stop there
            }
            else if(ch == '\n') {                   // If the data was a newline
                keepLooping = false;                // Stop at the current position.
            }
            else {                                  // If the data was neither a newline nor at the 0 byte
                fin.seekg(-2,ios_base::cur);        // Move to the front of that data, then to the front of the data before it
            }
        }

        string lastLine;            
        getline(fin,lastLine);                      // Read the current line
        cout << "Result: " << lastLine << '\n';     // Display it

        fin.close();
    }

    return 0;
}

下面是一个测试文件。成功处理文本文件中的空、单行和多行数据。

This is the first line.
Some stuff.
Some stuff.
Some stuff.
This is the last line.

【讨论】:

  • 所以,我实际上对此进行了测试,但它实际上并不起作用。 lastLine 始终为空。
  • 有趣,我在发帖前测试了它。你的 test.txt 结尾有多余的空行吗?
  • 直到我在第一次调用seekg 函数时替换了-1,即fin.seekg(-1,ios_base::end); 就在bool keepLooping = true; 的顶部,这对我也不起作用-2.
  • @llualpu,我也遇到了同样的问题。但你的建议奏效了。
【解决方案2】:

跳到然后结束,然后开始向后阅读块,直到找到任何一条线的标准。如果最后一个块没有以一行“结束”,您可能还需要尝试向前扫描(假设主动附加到文件中的行非常长)。

【讨论】:

  • 你到底是如何跳到最后并开始倒读的?
  • @user788171 通过使用类似 istream::seekg(0, ios_base::end) 的东西。然后,您可以从那里使用 seekg 在流中向前/向后移动。
【解决方案3】:

最初这是为了读取最后一个 syslog 条目而设计的。鉴于 EOF 之前的最后一个字符是 '\n',我们会回溯以找到下一个出现的 '\n',然后我们将该行存储到一个字符串中。

#include <fstream>
#include <iostream>

int main()
{
  const std::string filename = "test.txt";
  std::ifstream fs;
  fs.open(filename.c_str(), std::fstream::in);
  if(fs.is_open())
  {
    //Got to the last character before EOF
    fs.seekg(-1, std::ios_base::end);
    if(fs.peek() == '\n')
    {
      //Start searching for \n occurrences
      fs.seekg(-1, std::ios_base::cur);
      int i = fs.tellg();
      for(i;i > 0; i--)
      {
        if(fs.peek() == '\n')
        {
          //Found
          fs.get();
          break;
        }
        //Move one character back
        fs.seekg(i, std::ios_base::beg);
      }
    }
    std::string lastline;
    getline(fs, lastline);
    std::cout << lastline << std::endl;
  }
  else
  {
    std::cout << "Could not find end line character" << std::endl;
  }
  return 0;
}

【讨论】:

    【解决方案4】:

    虽然derpface 的答案绝对正确,但它经常返回意想不到的结果。这样做的原因是,至少在我的操作系统(Mac OSX 10.9.5)上,许多文本编辑器使用“结束行”字符终止其文件。

    例如,当我打开 vim 时,只输入单个字符 'a'(不返回),然后保存,文件现在将包含(十六进制):

    61 0A
    

    其中 61 是字母“a”,0A 是行尾字符。

    这意味着derpface的代码将对此类文本编辑器创建的所有文件返回一个空字符串。

    虽然我当然可以想象以“结束行”结尾的文件应该返回空字符串的情况,但我认为在处理常规文本文件时忽略最后一个“结束行”字符会更合适;如果文件以“end line”字符终止,我们会正确地忽略它,如果文件没有以“end line”字符终止,我们不需要检查它。

    我忽略输入文件最后一个字符的代码是:

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <iomanip>
    
    int main() {
        std::string result = "";
        std::ifstream fin("test.txt");
    
        if(fin.is_open()) {
            fin.seekg(0,std::ios_base::end);      //Start at end of file
            char ch = ' ';                        //Init ch not equal to '\n'
            while(ch != '\n'){
                fin.seekg(-2,std::ios_base::cur); //Two steps back, this means we
                                                  //will NOT check the last character
                if((int)fin.tellg() <= 0){        //If passed the start of the file,
                    fin.seekg(0);                 //this is the start of the line
                    break;
                }
                fin.get(ch);                      //Check the next character
            }
    
            std::getline(fin,result);
            fin.close();
    
            std::cout << "final line length: " << result.size() <<std::endl;
            std::cout << "final line character codes: ";
            for(size_t i =0; i<result.size(); i++){
                std::cout << std::hex << (int)result[i] << " ";
            }
            std::cout << std::endl;
            std::cout << "final line: " << result <<std::endl;
        }
    
        return 0;
    }
    

    将输出:

    final line length: 1
    final line character codes: 61 
    final line: a
    

    在单个“a”文件上。

    编辑:如果文件太大(> 2GB),if((int)fin.tellg() &lt;= 0){ 行实际上会导致问题,因为tellg 不仅仅返回文件开头的字符数(tellg() function give wrong size of file?)。最好分别测试文件的开头fin.tellg()==tellgValueForStartOfFile 和错误fin.tellg()==-1tellgValueForStartOfFile 可能为 0,但更好的确保方法可能是:

    fin.seekg (0, is.beg);
    tellgValueForStartOfFile = fin.tellg();
    

    【讨论】:

      【解决方案5】:

      你可以使用 seekg() 跳转到文件末尾,并向后读取,伪代码如下:

      ifstream fs
      fs.seekg(ios_base::end)
      bytecount = fs.tellg()
      index = 1
      while true
          fs.seekg(bytecount - step * index, ios_base::beg)
          fs.read(buf, step)
          if endlinecharacter in buf
              get endlinecharacter's index, said ei
              fs.seekg(bytecount - step*index + ei)
              fs.read(lastline, step*index - ei)
              break
          ++index
      

      【讨论】:

        【解决方案6】:

        我也在这个问题上苦苦挣扎,因为我运行了 uberwulu 的代码并且也得到了空白行。 这是我发现的。我以以下 .csv 文件为例:

        date       test1  test2
        20140908       1      2
        20140908      11     22
        20140908     111    235
        

        要理解代码中的命令,请注意以下位置及其对应的字符。 (Loc, char) : ... (63,'3') , (64,'5') , (65,-) , (66,'\n'), (EOF,-)。

        #include<iostream>
        #include<string>
        #include<fstream>
        
        using namespace std;
        
        int main()
        {
            std::string line;
            std::ifstream infile; 
            std::string filename = "C:/projects/MyC++Practice/Test/testInput.csv";
            infile.open(filename);
        
            if(infile.is_open())
            {
                char ch;
                infile.seekg(-1, std::ios::end);        // move to location 65 
                infile.get(ch);                         // get next char at loc 66
                if (ch == '\n')
                {
                    infile.seekg(-2, std::ios::cur);    // move to loc 64 for get() to read loc 65 
                    infile.seekg(-1, std::ios::cur);    // move to loc 63 to avoid reading loc 65
                    infile.get(ch);                     // get the char at loc 64 ('5')
                    while(ch != '\n')                   // read each char backward till the next '\n'
                    {
                        infile.seekg(-2, std::ios::cur);    
                        infile.get(ch);
                    }
                    string lastLine;
                    std::getline(infile,lastLine);
                    cout << "The last line : " << lastLine << '\n';     
                }
                else
                    throw std::exception("check .csv file format");
            }
            std::cin.get();
            return 0;
        }  
        

        【讨论】:

          【解决方案7】:

          我采用了 alexandros 的解决方案并对其进行了一些修饰

          bool moveToStartOfLine(std::ifstream& fs)
          {
              fs.seekg(-1, std::ios_base::cur);
              for(long i = fs.tellg(); i > 0; i--)
              {
                  if(fs.peek() == '\n')
                  {
                      fs.get();
                      return true;
                  }
                  fs.seekg(i, std::ios_base::beg);
              }
              return false;
          }
          
          std::string getLastLineInFile(std::ifstream& fs)
          {
              // Go to the last character before EOF
              fs.seekg(-1, std::ios_base::end);
              if (!moveToStartOfLine(fs))
                  return "";
          
              std::string lastline = "";
              getline(fs, lastline);
              return lastline;
          }
          
          int main()
          {
              const std::string filename = "test.txt";
              std::ifstream fs;
              fs.open(filename.c_str(), std::fstream::in);
              if(!fs.is_open())
              {
                  std::cout << "Could not open file" << std::endl;
                  return -1;
              }
          
              std::cout << getLastLineInFile(fs) << std::endl;
          
              return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 2010-10-15
            • 2012-08-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-29
            • 1970-01-01
            相关资源
            最近更新 更多