8

I need to read the nth line of a text file (e.g. textfile.findline(0) would find the first line of the text file loaded with ifstream textfile). Is this possible? I don't need to put the contents of the file in an array/vector, I need to just assign a specific line of the text file to a varible (specifically a int).

P.S. I am looking for the simplest solution that would not require me to use any big external library (e.g. Boost) Thanks in advance.

David Nehme
  • 21,379
  • 8
  • 78
  • 117
MrJackV
  • 547
  • 3
  • 5
  • 12
  • You could wrap this in a loop and use a counter: http://stackoverflow.com/questions/3910326/c-read-file-line-by-line-then-split-each-line-using-the-delimiter/3910610#3910610 - But you may want to store the entire file contents in memory so that if you perform multiple lookups, it will be tremendously faster. – jweyrich Sep 01 '11 at 16:29

4 Answers4

8

How about this?

std::string ReadNthLine(const std::string& filename, int N)
{
   std::ifstream in(filename.c_str());

   std::string s;
   //for performance
   s.reserve(some_reasonable_max_line_length);    

   //skip N lines
   for(int i = 0; i < N; ++i)
       std::getline(in, s);

   std::getline(in,s);
   return s; 
}
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
6

If you want to read the start of the nth line, you can use stdin::ignore to skip over the first n-1 lines, then read from the next line to assign to the variable.

template<typename T>
void readNthLine(istream& in, int n, T& value) {
  for (int i = 0; i < n-1; ++i) {
    in.ignore(numeric_limits<streamsize>::max(), '\n');
  }
  in >> value;
}
David Nehme
  • 21,379
  • 8
  • 78
  • 117
2

Armen's solution is the correct answer, but I thought I'd throw out an alternative, based on jweyrich's caching idea. For better or for worse, this reads in the entire file at construction, but only saves the newline positions (doesn't store the entire file, so it plays nice with massive files.) Then you can simply call ReadNthLine, and it will immediately jump to that line, and read in the one line you want. On the other hand, this is only optimal if you want to get only a fraction of the lines at a time, and the line numbers are not known at compile time.

class TextFile {
    std::ifstream file_stream;
    std::vector<std::ifstream::streampos> linebegins;
    TextFile& operator=(TextFile& b) = delete;
public;
    TextFile(std::string filename) 
    :file_stream(filename) 
    {
        //this chunk stolen from Armen's, 
        std::string s;
        //for performance
        s.reserve(some_reasonable_max_line_length); 
        while(file_stream) {
            linebegins.push_back(file_stream.tellg());
            std::getline(file_stream, s);
        }
    }
    TextFile(TextFile&& b) 
    :file_stream(std::move(b.file_stream)), 
    :linebegins(std::move(b.linebegins))
    {}
    TextFile& operator=(TextFile&& b) 
    {
        file_stream = std::move(b.file_stream);
        linebegins = std::move(b.linebegins);
    }
    std::string ReadNthLine(int N) {
        if (N >= linebegins.size()-1)
            throw std::runtime_error("File doesn't have that many lines!");
        std::string s;
        // clear EOF and error flags
        file_stream.clear();
        file_stream.seekg(linebegins[N]);
        std::getline(file_stream, s);
        return s;
    }
};
sjrowlinson
  • 3,297
  • 1
  • 18
  • 35
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Clever! This is the best solution IMO. – jweyrich Sep 01 '15 at 17:56
  • I have found issues with jumping to an arbitrary position using seekg() when the file is opened in text mode and it is saved in MS-Windows file format. This is due to the use of different end of line terminators. Mooning Duck's solution will work fine should the file be saved in unix file format (in vim do: set ff=unix). You will find a related discussion here: "[problem with seekg](https://www.daniweb.com/programming/software-development/threads/110602/problem-with-seekg)". – npras Jun 08 '16 at 14:17
  • @npras: Theoretically this shouldn't have that issue because I'm not calculating the offsets myself, but am using `tellg()`, which should always match `seekg`, even on Windows. – Mooing Duck Jun 08 '16 at 16:30
1

It's certainly possible. There are (n-1) '\n' characters preceding the nth line. Read lines until you reach the one you're looking for. You can do this on the fly without storing anything except the current line being considered.

Gian
  • 13,735
  • 44
  • 51