How do I also read a new line using C++ >> operator?
ifstream input(\"doc.txt\".c_str());
vector contents;
while (input >> word) {
conten
If you're looking to specifically use operator>>
and you don't technically need to use strings specifically, you can simply make a custom class with the behavior you want when it's read in from an istream
. It can even be (mostly) a wrapper for a string, with custom behavior when reading initial whitespace.
class StringAndNewline{
std::string str_;
friend std::istream& operator>>(std::istream& in, StringAndNewline& str);
public:
StringAndNewline() : str_(){}
StringAndNewline(std::string str) : str_(str){}
const std::string& str() const noexcept {return str_;}
std::string release() {return std::move(str_);}
};
The string read in operator automatically ignores all preceding whitespace to a sequence of non-whitespace characters, as defined by the present locale. This is the behavior you want to change, and as it turns out it's pleasantly simple to do so.
Disposal of the initial whitespace is commonly preformed by something called a sentry object, which also checks that the stream is valid and sets the stream's failbit
if it's at the end of the file. While its default behavior is to consume whitespace until it encounters a non-whitespace character, this is controlled by a flag in its constructor, so we can use that very nice encapsulated stream validity check it offers.
The string overload of operator>>
makes and checks a sentry, then reads until it encounters whitespace, the end of the stream, or a read fails. We can simply ensure that its sentry never encounters whitespace by dealing with it ourselves.
Thus the ultimate read-in structure for our custom class' custom operator>>
will look something like this:
Since we're only concerned with the '\n' characters in our whitespace that's simple too: just loop while the stream is valid (if it runs out of space before hitting either of our conditions, it sets failbit
like we would want) and exit the loop if one of two conditions are net: we get a newline character, or we get a non-whitespace character. Again, pleasantly simple:
std::istream& operator>>(std::istream& in, StringAndNewline& str){
std::istream::sentry sentry{in, true}; // make a sentry that doesn't eat whitespace
if(!sentry){return in;} // check the sentry
std::locale
presentLocale{}; // get the present locale
char presentChar;
while(in.get(presentChar)){ // while the stream is valid
if(presentChar == '\n'){ // if we get a newline
str.str_ = "\\n"; // set the string to an escaped newline
break; // exit the loop
}
// if we get a non-whitespace character
else if(!std::isspace(presentChar, presentLocale)){
in.unget(); // replace the character in the stream
in >> str.str_; // take advantage of the existing string operator
break; // done with loop
}
}
return in; // return the istream, whatever state it might be in
}
Once this is done, we set up an ostream operator for ease of printing:
std::ostream& operator<<(std::ostream& out, const StringAndNewline& str){
return out << str.str();
}
and test our code:
int main (){
std::istringstream file(
"hello\n"
"world\n"
"C++ is the best tool"
);
StringAndNewline
wordOrNewline;
while(file >> wordOrNewline){
std::cout << wordOrNewline << '\n';
}
}
which prints this:
hello
\n
world
\n
C++
is
the
best
tool
just like we wanted! Live on Coliru
You could even write a string operator if you really wanted to to easily convert the wrapper class to strings, but I'll leave that to you.