Newb question: what is the best way to get a char iterator over a text file?
I tried:
std::fstream csvSource (fileName);
auto aChar = csvSource.begin();
while (aChar != csvSource.end())
{
switch (*aChar)
{
case '"':
//and so on
but the compiler complains that fstream doesn't have a begin method.
Note, that I can't do it line-by-line, because newline characters that are within quotes are treated differently (literaly) than the other new line characters.
Use the >> operator from the ifstream class
std::ifstream csvSource (fileName);
csvSource >> noskipws;
char c;
while (csvSource>>c)
{
switch (c)
{
case '"':
//and so on
If you don't want to do fancy stuff with your iterators, that's the simplest way
If you have to use iterators, your best bet is istreambuf_iterator which is more optimal than istream_iterator when iterating chars.
Is there any particular reason though why you need to use iterators at all? They are there for the benefit of when you want to invoke an algorithm that requires them. But that isn't the case here as you are just looping.
You could just read in a char with get(). This might be better than operator>> which does a formatted read in, and will skip whitespace (which you might not want) unless you set skipws flag to false (I think it's noskipws) and may well be slightly less efficient.
Try std::istream_iterator. A very low level approach using raw loops could look like this:
std::fstream csvSource (fileName);
typedef std::istream_iterator<char> CharIter;
for (CharIter it(csvSource); it != CharIter(); ++it)
{
/* process *it */
char c = *it;
doSomething(c);
}
You can also use those iterators in all standard algorithms and handcrafted algorithms, which is preferable to raw loops. For example, to print all characters in the file to std::cout you can use
std::copy(CharIter{csvSource}, CarIter{}, std::ostream_iterator(std::cout));
I think you're looking for std::istreambuf_iterator as
#include <iterator> //for std::istreambuf_iterator
#include <algorithm> //for std::for_each
std:: istreambuf_iterator<char> begin(csvSource), end;
std::for_each(begin, end, [](char c)
{
switch(c)
{
//your cases and code
}
});
Or you can simply write csvSource >> c to read char-by-char. Both approaches are good. Which one to use, depends on situation.
Related
I have an issue where I cannot get my C++ program to read double digit integers.
My idea is to read it as string and then somehow parse it into separate integers and insert them into an array, but I am stuck on getting the code to read digits properly.
Sample Output:
i: 0 codeColumn 0
i: 1 codeColumn 1
i: 2 codeColumn 0 0
i: 3 codeColumn 0
i: 4 codeColumn 31 0
i: 5 codeColumn 1
i: 6 codeColumn 43 0
i: 7 codeColumn 3
i: 8 codeColumn 9 0
So the file is basically a line of triplets delimited by a comma:
0,1,0 0,0,31 0,0,18 0,0,8 0,11,0
My question is how do you get the trailing zeroes (see above) to move to a new line? I tried using "char" and a bunch of if statements to concatenate the single digits into double digits, but I feel like that's not really efficient or ideal. Any ideas?
My code:
#include <iostream> // Basic I/O
#include <string> // string classes
#include <fstream> // file stream classes
#include <sstream>
#include <vector>
int main()
{
ifstream fCode;
fCode.open("code.txt");
vector<string> codeColumn;
while (getline(fCode, codeLine, ',')) {
codeColumn.push_back(codeLine);
}
for (size_t i = 0; i < codeColumn.size(); ++i) {
cout << " i: " << i << " codeColumn " << codeColumn[i] << endl;
}
fCode.close();
}
getline(fCode, codeLine, ',')
is going to read between commas, so 0,1,0 0,0,31 will split up exactly as you have seen.
0,1,0 0,0,31
^ ^ ^ ^
The tokens collected are everything between the ^s
You have two delimiters you need to take into account comma and space. The easiest way to handle the space is with dumb old >>.
std::string triplet;
while (fCode >> triplet)
{
// do stuff with triplet. Maybe something like
std::istringstream strm(triplet); // make a stream out of the triplet
int a;
int b;
int c;
char sep1;
char sep2;
while (strm >> a >> sep1 >> b >> sep2 >> c // read all the tokens we want from triplet
&& sep1 == sep2 == ',') // and the separators are commas. Triplet is valid
{
// do something with a, b, and c
}
}
Documentation for std::istringstream.
So, I will show you 3 solutions from easy to understand C-Style code, then more-modern C++ code using the std::algorithm library and iterators, and, at the end an object oriented C++ solution.
I will also explain to you that std::getline can be, but should not be used for splitting strings into tokens.
I saw from your question that you had difficulties to understand that. And I understand your concern.
But let's start with an easy solution. I show the code and then explain it to you:
#include <iostream>
#include <fstream>
#include <string>
int main() {
// Open the source text file, and check, if there was no failure
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
size_t tripletCounter{ 0 };
// Now, read all triplets from the file in a simple for loop
for (std::string triplet{}; fCode >> triplet; ) {
// Prepare output
std::cout << "\ni:\t" << tripletCounter++ << "\tcodeColumn:\t";
// Go through the triplet, search for comma, then output the parts
for (size_t i{ 0U }, startpos{ 0U }; i <= triplet.size(); ++i) {
// So, if there is a comma or the end of the string
if ((triplet[i] == ',') || (i == (triplet.size()))) {
// Print substring
std::cout << (triplet.substr(startpos, i - startpos)) << ' ';
startpos = i + 1;
}
}
}
}
else {
std::cerr << "\n*** Error, Could not open source file\n";
}
return 0;
}
You see, we need just a few lines of easy to understand code that will fullfil your requirements and produce the desired output.
Some maybe for you new features:
The if statement with initializer. This is available since C++17. You can (in addition to the condition) define a variable and initalize it. So, in
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
we first define a variable with name "fCode" of type std::ifstream. We use the uniform initialzer "{}", to initialze it with the input file name.
This will call the constructor for the variable "fCode", and open the file. (This is was this constructor does). After the closing "}" of the "if-statement" the variable "fCode" will fall out of scope and the destructor for the std::ifstream will be called. This will close the file automatically.
This type of if-statement has been introduced to help to prevent name space solution. The variable shall only be visible in the scope, where it is used. Without that, you would have to define the std::ifstream outside (before) the if and it would be visible for the outer context and the file would be closed at a very late time. So, please get aquainted to that.
Next we define the a "tripletCounter". That is hust necessary for output. There is no other usage.
Then, again such an if-statement with initailizer. We first define an empty std::string "triplet" and then use the extractor operator to read text until the next white space. This is how the "extractor" (>>) works. We use the whole expression as condition, to check, if the extraction worlked, or if we hit the end of file (or some other error). This works because the extractor operator returns the stream in that is was working, so a reference to "fCode". And the stream has on overwritten boolen operator !, to check the condition of the stream. Please see here.
You should always and for every IO-Operation check, if it worked or not.
So, next we split the triple (e.g. "0,1,0") into its sub-strings with an very easy for loop. We go through all characters in the string and check, if the current chacter is a comma or the end of string. In that case, we output, the characters before the delimiter.
Very simple and easy to understand. std::getline is not needed here.
So, next solution, more advanced:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
std::regex re(",");
int main() {
// Open the source text file, and check, if there was no failure
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
size_t tripletCounter{ 0 };
// Now, read all triplets from the file into a vector
std::vector triplets(std::istream_iterator<std::string>(fCode), {});
// Next, go through all triplets
for (const std::string &triplet : triplets) {
// Prepare output
std::cout << "\ni:\t" << tripletCounter++ << "\tcodeColumn:\t";
// Split triplet into code column. All codes are in vector codeColums
std::vector codeColumns(std::sregex_token_iterator(triplet.begin(), triplet.end(), re, -1), {});
//Show codes
for (const std::string& code : codeColumns) std::cout << code << ' ';
}
}
else {
std::cerr << "\n*** Error, Could not open source file\n";
}
return 0;
}
The beginning is the same. But then:
// Now, read all triplets from the file into a vector
std::vector triplets(std::istream_iterator<std::string>(fCode), {});
UhOh. Whats that. Let's start with the std::istream_iterator. If you read the linked description, then you will find out, that it will basically call the extractor operator >> for the specified type. And since it is an iterator, it will call it again and again, if the iterator is incremented. Ok, understandable, but then
We define variable triplets as std::vector and call its constructor with 2 arguments. That constructor is the the so called range constructor of the std::vector. Please see the descrition for constructor 5. Aha, it gets a "begin()" iterator and an "end()" iterator. Aha, but what is this strange {} instead of the "end()"-iterator. This is the default initializer (please see here and here. And if we look at the description of the std::istream_iterator we can see the the default is the end iterator. OK, understood.
I assum that you know about the range based for, which comes next. Good. But now, we come to the most difficult point. Splitting a string with delimiters. People are using std::getline. But why? Why are people doing such strange stuff?
What do people expect from the function, when they read
getline ?
Most people would say, Hm, I guess it will read a complete line from somewhere. And guess what, that was the basic intention for this function. Read a line from a stream and put it into a string.
As you can see here std::getline has some additional functionality.
And this lead to a major misuse of this function for splitting up std::strings into tokens.
Splitting strings into tokens is a very old task. In very early C there was the function strtok, which still exists, even in C++. Please see std::strtok.
But because of the additional functionality of std::getline is has been heavily misused for tokenizing strings. If you look on the top question/answer regarding how to parse a CSV file (please see here), then you will see what I mean.
People are using std::getline to read a text line, a string, from the original stream, then stuffing it into an std::istringstream again and use std::getline with delimiter again to parse the string into tokens.
Weird.
Because, since many many years, we have a dedicated, special function for tokenizing strings, especially and explicitly designed for that purpose. It is the
std::sregex_token_iterator
And since we have such a dedicated function, we should simply use it.
This thing is an iterator. For iterating over a string, hence the function name is starting with an s. The begin part defines, on what range of input we shall operate, (begin(), end()), then there is a std::regex for what should be matched / or what should not be matched in the input string. The type of matching strategy is given with last parameter.
0 --> give me the stuff that I defined in the regex and
-1 --> give me that what is NOT matched based on the regex.
We can use this iterator for storing the tokens in a std::vector. The std::vector has a range constructor, which takes 2 iterators as parameter, and copies the data between the first iterator and 2nd iterator to the std::vector. The statement
std::vector tokens(std::sregex_token_iterator(s.begin(), s.end(), re, -1), {});
defines a variable “tokens” as a std::vector and uses again the range-constructor of the std::vector. Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction"). I also used that for the vector above.
Additionally, you can see that I do not use the "end()"-iterator explicitly.
This iterator will be constructed from the empty brace-enclosed default initializer with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that, as already described.
You can read any number of tokens in a line and put it into the std::vector
But you can do even more. You can validate your input. If you use 0 as last parameter, you define a std::regex that even validates your input. And you get only valid tokens.
Overall, the usage of a dedicated functionality is superior over the misused std::getline and people should simply use it.
Some people may complain about the function overhead, but how many of them are using big data. And even then, the approach would be probably then to use string.findand string.substring or std::stringviews or whatever.
So, somehow advanced, but you will eventually learn it.
And now we will use an object oriented approach. As you know, C++ is an object oriented language.
We can put data, and methods working with that data, in a class (struct). The functionality is encapsulated. Only the class should know, how to operate on its data. Sw, we will define a class "Code". This contains a std::array consisting of 3 st::strings. and associated functions. For the array we made a typedef for easier writing. The functions that we need, are input and output. So, we will overwrite the extractor and the inserter operator.
In these operators, we use functions as dscribed above.
And as a result of all this work, we get an elegant main function, where all the work is done in 3 lines of code.
Please see:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <regex>
#include <array>
#include <algorithm>
using Triplet = std::array<std::string, 3>;
std::regex re(",");
struct Code {
// Our Data
Triplet triplet{};
// Overwrite extractor operator for easier input
friend std::istream& operator >> (std::istream& is, Code& c) {
// Read a triplet with commans
if (std::string s{}; is >> s) {
// Copy the single columns of the triplet in to our internal Data structure
std::copy(std::sregex_token_iterator(s.begin(), s.end(), re, -1), {}, c.triplet.begin());
}
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Code& c) {
return os << c.triplet[0] << ' ' << c.triplet[1] << ' ' << c.triplet[2];
}
};
int main() {
// Open the source text file, and check, if there was no failure
if (std::ifstream fCode{ "r:\\code.txt" }; fCode) {
// Now, read all triplets from the file, split it and put the Codes into a vector
std::vector code(std::istream_iterator<Code>(fCode), {});
// Show output
for (size_t tripletCounter{ 0U }; tripletCounter < code.size(); tripletCounter++)
std::cout << "\ni:\t" << tripletCounter << "\tcodeColumn:\t" << code[tripletCounter];
}
else {
std::cerr << "\n*** Error, Could not open source file\n";
}
return 0;
}
Suppose you have two strings and you want to know whether one is a prefix of the other. Here is one way to do it in C++:
std::string s = ...;
std::string prefix = ...;
bool sStartsWithPrefix = std::equal(prefix.begin(), prefix.end(), s.begin());
But suppose that instead of s you have an input stream:
std::istream& inputStream = ...;
std::string prefix = ...;
bool inputStreamStartsWithPrefix = ?
Sure, we could compute inputStreamStartsWithPrefix by writing a for loop. But how to do it idiomatically (without a for loop, with an algorithm)?
You can use a std::istream_iterator in combination with std::equal like
std::istringstream is("foobar");
std::string prefix = "foo";
bool inputStreamStartsWithPrefix = std::equal(prefix.begin(), prefix.end(), std::istream_iterator<char>(is));
You're going to have to read from the stream anyways, so you're probably better off just reading the from stream, and then checking the contents of what you just read to see if it begins with the string.
Trying to do anything else will make it very difficult to do whatever you intend to do when the stream doesn't start with that string. (unless, of course, if your intention is something like quitting the program)
I'm trying to move from stdio to iostream, which is proving very difficult. I've got the basics of loading a file and closing them, but I really don't have a clue as to what a stream even is yet, or how they work.
In stdio everything's relatively easy and straight forward compared to this. What I need to be able to do is
Read a single character from a text file.
Call a function based on what that character is.
Repeat till I've read all the characters in the file.
What I have so far is.. not much:
int main()
{
std::ifstream("sometextfile.txt", std::ios::in);
// this is SUPPOSED to be the while loop for reading. I got here and realized I have
//no idea how to even read a file
while()
{
}
return 0;
}
What I need to know is how to get a single character and how that character is actually stored(Is it a string? An int? A char? Can I decide for myself how to store it?)
Once I know that I think I can handle the rest. I'll store the character in an appropriate container, then use a switch to do things based on what that character actually is. It'd look something like this.
int main()
{
std::ifstream textFile("sometextfile.txt", std::ios::in);
while(..able to read?)
{
char/int/string readItem;
//this is where the fstream would get the character and I assume stick it into readItem?
switch(readItem)
{
case 1:
//dosomething
break;
case ' ':
//dosomething etc etc
break;
case '\n':
}
}
return 0;
}
Notice that I need to be able to check for white space and new lines, hopefully it's possible. It would also be handy if instead of one generic container I could store numbers in an int and chars in a char. I can work around it if not though.
Thanks to anyone who can explain to me how streams work and what all is possible with them.
You also can abstract away the whole idea of getting a single character with streambuf_iterators, if you want to use any algorithms:
#include <iterator>
#include <fstream>
int main(){
typedef std::istreambuf_iterator<char> buf_iter;
std::fstream file("name");
for(buf_iter i(file), e; i != e; ++i){
char c = *i;
}
}
You can also use standard for_each algorithm:
#include <iterator>
#include <algorithm>
#include <fstream>
void handleChar(const char& c)
{
switch (c) {
case 'a': // do something
break;
case 'b': // do something else
break;
// etc.
}
}
int main()
{
std::ifstream file("file.txt");
if (file)
std::for_each(std::istream_iterator<char>(file),
std::istream_iterator<char>(),
handleChar);
else {
// couldn't open the file
}
}
istream_iterator skips whitespace characters. If those are meaningful in your file use istreambuf_iterator instead.
This has already been answered but whatever.
You can use the comma operator to create a loop which behaves like a for each loop which goes through the entire file reads every character one by one and stop when it's done.
char c;
while((file.get(c), file.eof()) == false){
/*Your switch statement with c*/
}
Explanation:
The first part of the expression in the for loop (file.get(c), file.eof())
will function as follows. Firstly file.get(c) gets executed which reads a character and stores the result in c. Then, due to the comma operator, the return value is discarded and file.eof() gets executed which returns a bool whether or not the end of the file has been reached. This value is then compared.
Side Note:
ifstream::get() always reads the next character. Which means calling it twice would read the first two character in the file.
fstream::get
Next time you have similar problem go to cplusplusreference or similar site, locate class you have problem with and read description of every method. Normally, this solves the problem. Googling also works.
I would honestly just avoid iterators here since it's just hurting readability. Instead, consider:
int main()
{
std::ifstream file("sometextfile.txt")
char c;
while(file >> c) {
// do something with c
}
// file reached EOF
return 0;
}
This works because the stream implements operator bool, which makes it implicitly convertible to true if the stream hasn't reached EOF, and false if it has; and because file >> c returns the file itself, it can be used as the while condition.
Using an iterator is only really useful if you intend to use other functions from , but for plain reading, using the stream operator is simpler and easier to read.
while (textFile.good()) {
char a;
textFile.get(a);
switch(a)
{
case 1:
//dosomething
break;
case ' ':
//dosomething etc etc
break;
case '\n':
}
}
I've seen at least two ways of reading lines from a file in C++ tutorials:
std::ifstream fs("myfile.txt");
if (fs.is_open()) {
while (fs.good()) {
std::string line;
std::getline(fs, line);
// ...
and:
std::ifstream fs("myfile.txt");
std::string line;
while (std::getline(fs, line)) {
// ...
Of course, I can add a few checks to make sure that the file exists and is opened. Other than the exception handling, is there a reason to prefer the more-verbose first pattern? What's your standard practice?
while (std::getline(fs, line))
{}
This is not only correct but preferable also because it is idiomatic.
I assume in the first case, you're not checking fs after std::getline() as if(!fs) break; or something equivalent. Because if you don't do so, then the first case is completely wrong. Or if you do that, then second one is still preferable as its more concise and clear in logic.
The function good() should be used after you made an attempt to read from the stream; its used to check if the attempt was successful. In your first case, you don't do so. After std::getline(), you assume that the read was successful, without even checking what fs.good() returns. Also, you seem to assume that if fs.good() returns true, std::getline would successfully read a line from the stream. You're going exactly in the opposite direction: the fact is that, if std::getline successfully reads a line from the stream, then fs.good() would return true.
The documentation at cplusplus says about good() that,
The function returns true if none of the stream's error flags (eofbit, failbit and badbit) are set.
That is, when you attempt to read data from an input stream, and if the attempt was failure, only then a failure flag is set and good() returns false as an indication of the failure.
If you want to limit the scope of line variable to inside the loop only, then you can write a for loop as:
for(std::string line; std::getline(fs, line); )
{
//use 'line'
}
Note: this solution came to my mind after reading #john's solution, but I think its better than his version.
Read a detail explanation here why the second one is preferable and idiomatic:
Linux | Segmentation Fault in C++ - Due to the function ifstream
Or read this nicely written blog by #Jerry Coffin:
Reading files
Think of this as an extended comment to Nawaz' already excellent answer.
Regarding your first option,
while (fs.good()) {
std::string line;
std::getline(fs, line);
...
This has multiple problems. Problem number 1 as that that the while condition is in the wrong place and is superfluous. It's in the wrong place because fs.good() indicates whether or not the most recent action performed on the file was OK. A while condition should be with respect to the upcoming actions, not the previous ones. There is no way to know whether the upcoming action on the file will be OK. What upcoming action? fs.good() does not read your code to see what that upcoming action is.
Problem number two is that the you are ignoring the return status from std::getline(). That's OK if you immediately check the status with fs.good(). So, fixing this up a bit,
while (true) {
std::string line;
if (std::getline(fs, line)) {
...
}
else {
break;
}
}
Alternatively, you can do if (! std::getline(fs, line)) { break; } but now you have a break in the middle of the loop. Yech. It is much, much better to make the exit conditions a part of the loop statement itself if at all possible.
Compare that to
std::string line;
while (std::getline(fs, line)) {
...
}
This is the standard idiom for reading lines from a file. A very similar idiom exists in C. This idiom is very old, very widely used, and very widely viewed as the correct way to read lines from a file.
What if you come from a shop that bans conditionals with side-effects? (There are lots and lots of programming standards that do just that.) There is a way around this without resorting to the break in the middle of the loop approach:
std::string line;
for (std::getline(fs, line); fs.good(); std::getline(fs, line)) {
...
}
Not as ugly as the break approach, but most will agree that this isn't nearly as nice-looking as is the standard idiom.
My recommendation is to use the standard idiom unless some standards idiot has banned its use.
Addendum
Regarding for (std::getline(fs, line); fs.good(); std::getline(fs, line)): This is ugly for two reasons. One is that obvious chunk of replicated code.
Less obvious is that calling getline and then good breaks atomicity. What if some other thread is also reading from the file? This isn't quite so important right now because C++ I/O currently is not threadsafe. It will be in the upcoming C++11. Breaking atomicity just to keep the enforcers of the standards happy is recipe for disaster.
Actually I prefer another way
for (;;)
{
std::string line;
if (!getline(myFile, line))
break;
...
}
To me it reads better, and the string is scoped correctly (i.e. inside the loop where it is being used, not outside the loop)
But of the two you've written the second is correct.
The first one deallocated and re-allocated the string every loop, wasting time.
The second time writes the string to an already existing space removing the deallocation and reallocation, making it actually faster (and better) than the first one.
Try this =>
// reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main () {
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
I'm trying to read an entire stream (multiple lines) into a string.
I'm using this code, and it works, but it's offending my sense of style... Surely there's an easier way? Maybe using stringstreams?
void Obj::loadFromStream(std::istream & stream)
{
std::string s;
std::streampos p = stream.tellg(); // remember where we are
stream.seekg(0, std::ios_base::end); // go to the end
std::streamoff sz = stream.tellg() - p; // work out the size
stream.seekg(p); // restore the position
s.resize(sz); // resize the string
stream.read(&s[0], sz); // and finally, read in the data.
Actually, a const reference to a string would do as well, and that may make things easier...
const std::string &s(... a miracle occurs here...)
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), {});
I'm late to the party, but here is a fairly efficient solution:
std::string gulp(std::istream &in)
{
std::string ret;
char buffer[4096];
while (in.read(buffer, sizeof(buffer)))
ret.append(buffer, sizeof(buffer));
ret.append(buffer, in.gcount());
return ret;
}
I did some benchmarking, and it turns out that the std::istreambuf_iterator technique (used by the accepted answer) is actually much slower. On gcc 4.4.5 with -O3, it's about a 4.5x difference on my machine, and the gap becomes wider with lower optimization settings.
You could do
std::string s;
std::ostringstream os;
os<<stream.rdbuf();
s=os.str();
but I don't know if it's more efficient.
Alternative version:
std::string s;
std::ostringstream os;
stream>>os.rdbuf();
s=os.str();
You can try using something from algorithms. I have to get ready for work but here's a very quick stab at things (there's got to be a better way):
copy( istreambuf_iterator<char>(stream), istreambuf_iterator<char>(), back_inserter(s) );
Well, if you are looking for a simple and 'readable' way to do it. I would recomend add/use some high level framework on your project. For that I's always use Poco and Boost on all my projects. In this case, with Poco:
string text;
FileStream fstream(TEXT_FILE_PATH);
StreamCopier::copyToString(fstream, text);
Perhaps this 1 line C++11 solution:
std::vector<char> s{std::istreambuf_iterator<char>{in},{}};
What about to use getline with delimiter? The next code helps me to read whole std::cin into string on ubuntu with g++-10.
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
getline(cin, s, {}); //the whole stream into variable s
return 0;
}