Reading Directly from an Std::Istream into an Std::String

Reading directly from an std::istream into an std::string

std::string has a resize function you could use, or a constructor that'll do the same:

boost::uint16_t len;
is.read((char*)&len, 2);

std::string str(len, '\0');
is.read(&str[0], len);

This is untested, and I don't know if strings are mandated to have contiguous storage.

Read whole ASCII file into C++ std::string

Update: Turns out that this method, while following STL idioms well, is actually surprisingly inefficient! Don't do this with large files. (See: http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html)

You can make a streambuf iterator out of the file and initialize the string with it:

#include <string>
#include <fstream>
#include <streambuf>

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

Not sure where you're getting the t.open("file.txt", "r") syntax from. As far as I know that's not a method that std::ifstream has. It looks like you've confused it with C's fopen.

Edit: Also note the extra parentheses around the first argument to the string constructor. These are essential. They prevent the problem known as the "most vexing parse", which in this case won't actually give you a compile error like it usually does, but will give you interesting (read: wrong) results.

Following KeithB's point in the comments, here's a way to do it that allocates all the memory up front (rather than relying on the string class's automatic reallocation):

#include <string>
#include <fstream>
#include <streambuf>

std::ifstream t("file.txt");
std::string str;

t.seekg(0, std::ios::end);
str.reserve(t.tellg());
t.seekg(0, std::ios::beg);

str.assign((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());

How to read entire stream into a std::string?

How about

std::istreambuf_iterator<char> eos;
std::string s(std::istreambuf_iterator<char>(stream), eos);

(could be a one-liner if not for MVP)

post-2011 edit, this approach is now spelled

std::string s(std::istreambuf_iterator<char>(stream), {});

How do I read a file in C++ and write contents into a string?

Don't use << to append something in a string.

Rather than:

while (getline(shaderFile, line)) {
sourceCode << line;
}

Consider:

while (getline(shaderFile, line)) {
sourceCode += line;
}

Reading formatted input from an istream

Although it isn't a priori obvious there is a relatively simple way to change what a stream considers to be whitespace. The way to do it is to imbue() the stream with a std::locale object whose std::ctype<char> facet is replaced to consider the desired characters as whitespace. imbue(), locale, ctype - huh?!? OK, well, these aren't necessarily the things you use day to day so here is a quick example which set's up std::cin to use comma and newline characters as spaced:

#include <locale>
template <char S0, char S1>
struct commactype_base {
commactype_base(): table_() {
this->table_[static_cast<unsigned char>(S0)] = std::ctype_base::space;
this->table_[static_cast<unsigned char>(S1)] = std::ctype_base::space;
}
std::ctype<char>::mask table_[std::ctype<char>::table_size];
};
template <char S0, char S1 = S0>
struct ctype:
commactype_base<S0, S1>,
std::ctype<char>
{
ctype(): std::ctype<char>(this->table_, false) {}
};

Actually, this particular implementation of std::ctype<char> can actually be used to use one or two arbitrary chars as spaces (a proper C++2011 version would probably allow an arbitrary number of arguments; also, the don't really have to be template argumentss). Anyway, with this in place, just drop the following line at the beginning of your main() function and you are all set:

std::cin.imbue(std::locale(std::locale(), new ::ctype<',', '\n'>));

Note that this really only considers , and \n as space characters. This also means that no other characters are skipped as whitespace. ... and, of course, a sequence of multiple comma characters is considered to be just one separator rather than possibly creating a bunch of empty strings. Also note that the above std::ctype<char> facet removes all other character classification. If you want to parse other objects than just strings you might want to retain the other character classification and only change that for spaces. Here is a way this could be done:

template <char S0, char S1>
struct commactype_base {
commactype_base(): table_() {
std::transform(std::ctype<char>::classic_table(),
std::ctype<char>::classic_table() + std::ctype<char>::table_size,
this->table_,
[](std::ctype_base::mask m) -> std::ctype_base::mask {
return m & ~(std::ctype_base::space);
});
this->table_[static_cast<unsigned char>(S0)] |= std::ctype_base::space;
this->table_[static_cast<unsigned char>(S1)] |= std::ctype_base::space;
}
std::ctype<char>::mask table_[std::ctype<char>::table_size];
};

Sadly, this crashes with the version of gcc I have on my system (apparently the std::ctype<char>::classic_table() yields a null pointer. Compiling this with a current version of clang doesn't work because clang doesn't support lambda. With the two caveats the above code should be correct, though...



Related Topics



Leave a reply



Submit