What is the most elegant way to read a text file with c++?

C++TextFile Io

C++ Problem Overview


I'd like to read whole content of a text file to a std::string object with c++.

With Python, I can write:

text = open("text.txt", "rt").read()

It is very simple and elegant. I hate ugly stuff, so I'd like to know - what is the most elegant way to read a text file with C++? Thanks.

C++ Solutions


Solution 1 - C++

There are many ways, you pick which is the most elegant for you.

Reading into char*:

ifstream file ("file.txt", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
    file.seekg(0, ios::end);
    size = file.tellg();
    char *contents = new char [size];
    file.seekg (0, ios::beg);
    file.read (contents, size);
    file.close();
    //... do something with it
    delete [] contents;
}

Into std::string:

std::ifstream in("file.txt");
std::string contents((std::istreambuf_iterator<char>(in)), 
    std::istreambuf_iterator<char>());

Into vector<char>:

std::ifstream in("file.txt");
std::vector<char> contents((std::istreambuf_iterator<char>(in)),
    std::istreambuf_iterator<char>());

Into string, using stringstream:

std::ifstream in("file.txt");
std::stringstream buffer;
buffer << in.rdbuf();
std::string contents(buffer.str());

file.txt is just an example, everything works fine for binary files as well, just make sure you use ios::binary in ifstream constructor.

Solution 2 - C++

There's another thread on this subject.

My solutions from this thread (both one-liners):

The nice (see Milan's second solution):

string str((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());

and the fast:

string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str());

Solution 3 - C++

You seem to speak of elegance as a definite property of "little code". This is ofcourse subjective in some extent. Some would say that omitting all error handling isn't very elegant. Some would say that clear and compact code you understand right away is elegant.

Write your own one-liner function/method which reads the file contents, but make it rigorous and safe underneath the surface and you will have covered both aspects of elegance.

All the best

/Robert

Solution 4 - C++

But beware that a c++-string (or more concrete: An STL-string) is as little as a C-String capable of holding a string of arbitraty length - of course not!

Take a look at the member max_size() which gives you the maximum number of characters a string might contain. This is an implementation definied number and may not be portable among different platforms. Visual Studio gives a value of about 4gigs for strings, others might give you only 64k and on 64Bit-platforms it might give you something really huge! It depends and of course normally you will run into a bad_alloc-exception due to memory exhaustion a long time before reaching the 4gig limit...

BTW: max_size() is a member of other STL-containers as well! It will give you the maximum number of elements of a certain type (for which you instanciated the container) which this container will (theoretically) be able to hold.

So, if you're reading from a file of unknow origin you should:

  • Check its size and make sure it's smaller than max_size()
  • Catch and process bad_alloc-exceptions

And another point: Why are you keen on reading the file into a string? I would expect to further process it by incrementally parsing it or something, right? So instead of reading it into a string you might as well read it into a stringstream (which basically is just some syntactic sugar for a string) and do the processing. But then you could do the processing directly from the file as well. Because if properly programmed the stringstream could seamlessly be replaced by a filestream, i. e. by the file itself. Or by any other input stream as well, they all share the same members and operators and can thus be seamlessly interchanged!

And for the processing itself: There's also a lot you can have automated by the compiler! E. g. let's say you want to tokenize the string. When defining a proper template the following actions:

  • Reading from a file (or a string or any other input stream)
  • Tokenizing the content
  • pushing all found tokens into an STL-container
  • sort the tokens alphabetically
  • eleminating any double values
    can all(!!) be achived in one single(!) line of C++-code (let aside the template itself and the error handling)! It's just a single call of the function std::copy()! Just google for "token iterator" and you'll get an idea of what I mean. So this appears to me to be even more "elegant" than just reading from a file...

Solution 5 - C++

I like Milan's char* way, but with std::string.


#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;




string& getfile(const string& filename, string& buffer) {
ifstream in(filename.c_str(), ios_base::binary | ios_base::ate);
in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
buffer.resize(in.tellg());
in.seekg(0, ios_base::beg);
in.read(&buffer[0], buffer.size());
return buffer;
}




int main(int argc, char* argv[]) {
if (argc != 2) {
cerr << "Usage: this_executable file_to_read\n";
return EXIT_FAILURE;
}
string buffer;
cout << getfile(argv[1], buffer).size() << "\n";
}

int main(int argc, char* argv[]) { if (argc != 2) { cerr << "Usage: this_executable file_to_read\n"; return EXIT_FAILURE; } string buffer; cout << getfile(argv[1], buffer).size() << "\n"; }

(with or without the ios_base::binary, depending on whether you want newlines tranlated or not. You could also change getfile to just return a string so that you don't have to pass a buffer string in. Then, test to see if the compiler optimizes the copy out when returning.)

However, this might look a little better (and be a lot slower):


#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;




string getfile(const string& filename) {
ifstream in(filename.c_str(), ios_base::binary);
in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
return string(istreambuf_iterator<char>(in), istreambuf_iterator<char>());
}




int main(int argc, char* argv[]) {
if (argc != 2) {
cerr << "Usage: this_executable file_to_read\n";
return EXIT_FAILURE;
}
cout << getfile(argv[1]).size() << "\n";
}

int main(int argc, char* argv[]) { if (argc != 2) { cerr << "Usage: this_executable file_to_read\n"; return EXIT_FAILURE; } cout << getfile(argv[1]).size() << "\n"; }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFang-Pen LinView Question on Stackoverflow
Solution 1 - C++Milan BabuškovView Answer on Stackoverflow
Solution 2 - C++Konrad RudolphView Answer on Stackoverflow
Solution 3 - C++sharkinView Answer on Stackoverflow
Solution 4 - C++Don PedroView Answer on Stackoverflow
Solution 5 - C++Shadow2531View Answer on Stackoverflow