c++ fastest way to read only last line of text file?

前端 未结 7 486
温柔的废话
温柔的废话 2020-11-27 20:35

I would like to read only the last line of a text file (I\'m on UNIX, can use Boost). All the methods I know require scanning through the entire file to get the last line wh

相关标签:
7条回答
  • 2020-11-27 21:08

    I took alexandros' solution and spruced it up a bit

    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;
    }
    
    0 讨论(0)
  • 2020-11-27 21:10

    Use seekg to jump to the end of the file, then read back until you find the first newline. Below is some sample code off the top of my head using 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;
    }
    

    And below is a test file. It succeeds with empty, one-line, and multi-line data in the text file.

    This is the first line.
    Some stuff.
    Some stuff.
    Some stuff.
    This is the last line.
    
    0 讨论(0)
  • 2020-11-27 21:10

    You can use seekg() to jump to the end of file, and read backward, the Pseudo-code is like:

    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
    
    0 讨论(0)
  • 2020-11-27 21:14

    Initially this was designed to read the last syslog entry. Given that the last character before the EOF is '\n' we seek back to find the next occurrence of '\n' and then we store the line into a string.

    #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;
    }
    
    0 讨论(0)
  • 2020-11-27 21:16

    I was also struggling on the problem because I ran uberwulu's code and also got blank line. Here is what I found. I am using the following .csv file as an example:

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

    To understand the commands in the code, please notice the following locations and their corresponding chars. (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;
    }  
    
    0 讨论(0)
  • 2020-11-27 21:18

    Jump to then end, and start reading blocks backwards until you find whatever your criteria for a line is. If the last block doesn't "end" with a line, you'll probably need to try and scan forward as well (assuming a really long line in an actively appended to file).

    0 讨论(0)
提交回复
热议问题