Why does the following program output
1 2 3 4 4 4
and not
1 2 3 4 5 6
for each of the values provided?
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <sstream>
int main()
{
std::vector<int> numbers;
std::stringstream ss;
ss << " 1 2";
std::istream_iterator<int> start{ss},end;
ss << " 3 4";
numbers.push_back(*start++);
numbers.push_back(*start++);
numbers.push_back(*start++);
ss << " 5 6";
numbers.push_back(*start++);
numbers.push_back(*start++);
numbers.push_back(*start++);
std::cout << "numbers read in:\n";
for (auto number : numbers) {
std::cout << number << " ";
}
std::cout << "\n";
}
Its not iterator doing as you might have thought. It's ss that is invalidated after iterator progressing. Initialiy stringstream constains 1 2 3 4 and is in valid state. But is invalidated by the third iterator dereference, so next operation ss << " 5 6" fails. To fix this, clear flags of stringstream variable:
//...
ss.clear();
ss << " 5 6";
//...
Output:
numbers read in:
1 2 3 4 5 6
Use stream iterators with some caution. When a valid istream_iterator reaches the end of the underlying stream, it becomes equal to the end-of-stream iterator.
And then dereferencing or incrementing it further invokes undefined behavior, in your case you just got a copy of the most recently read object.
Also keep in mind that the first object from the stream is read when the iterator is constructed.
Related
I'm trying to read in this text file:
8 4 4 6 1
8 4 4 6 2
8 4 4 6 3
8 4 4 6 4
8 4 4 6 5
8 4 4 6 6
8 4 4 6 7
8 4 4 6 8
11 4 4 6 3
15 11 13
7 2 1 4 4
9 4 3 9 9
8 2 1 5 4
10 1 2 3 4 6 1
6 1 1 2 5 3 2
13 1 1 2 10 3 8
11 2 11 10 7
And printing it exactly as shown to the console (to make sure I got every input).
However, for some reason my code crashes after reading in the first line. I can't even terminate the debugger.
Here's my code:
while(getline(inFile, buffer)){
buffer2 = strdup(buffer.c_str());
line = strtok(buffer2, " ");
size = atoi(line);
cout << size << " ";
while(line!=NULL){
line = strtok(NULL, " ");
cout << line << " ";
}
cout << "~~~~~~~~~" << endl;
}
If you are going to use C++ you should take advantage of that, use string streams:
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std; //for sample purposes, should not be used
int main() {
int temp, count = 0, sum = 0, total = 0;
string buffer;
ifstream myFile("in.txt");
if (!myFile.is_open())
cout << "No file" << endl;
else{
while(getline(myFile, buffer)){
sum = 0;
stringstream ss(buffer);
while(ss >> temp){
count++; //number count
sum += temp; //line sum
cout << temp << " ";
}
total += sum; //total sum
cout << endl << "count: " << count << endl
<< "sum: " << sum << endl << "total: " << total << endl << endl;
}
myFile.close();
}
cout << "~~~~~~~~~" << endl;
}
You are leaking the memory allocated by strdup(). You need to call free() when you are done using buffer2.
But more importantly, strtok() returns NULL when there are no more tokens to return. But it is undefined behavior to pass a NULL char* pointer to operator<<. Your while loop is doing exactly that when it reaches the end of each line, so anything could happen, including crashing.
Try this instead:
while (getline(inFile, buffer)) {
buffer2 = strdup(buffer.c_str());
if (buffer2 != NULL) {
line = strtok(buffer2, " ");
while (line != NULL) {
size = atoi(line);
cout << size << " ";
line = strtok(NULL, " ");
}
free(buffer2);
}
cout << "~~~~~~~~~" << endl;
}
That being said, why are you using strdup(), strtok(), and atoi() at all? You are writing C++ code, you should C++ semantics instead of C semantics. For example, you can use std::istringstream instead, eg:
while (getline(inFile, buffer)) {
istringstream iss(buffer);
while (iss >> size) {
cout << size << " ";
}
cout << "~~~~~~~~~" << endl;
}
As always, there are many possible solutions. I would like to show an additional one. This is using more modern C++ elements, mainly from the algorithm and iterator library.
So, what will we do?
First we read each line as a std::string in a simple for loop with std::getline. Then we will put the line again in a std::istringstream so that we can take advantage of C++ iterator: std::istream_iterator.
This iterator will iterate over the elements in the string and extract all integers. It is like calling the extractor operator ( >> ) for all elements in the line string.
We use the iterator in the so called range constructor of os a std::vector. This inplace created vector, will be added to the destiantion data. So, as a result, we will get vector of vector of int: A 2-dimensional vector.
For debug purposes, we copy each row of intes to std::cout.
Please note that we do really need only very few and very simple statements to fulfill the task.
Please check.
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
#include <iterator>
std::istringstream sourceFile{R"(8 4 4 6 1
8 4 4 6 2
8 4 4 6 3
8 4 4 6 4
8 4 4 6 5
8 4 4 6 6
8 4 4 6 7
8 4 4 6 8
11 4 4 6 3
15 11 13
7 2 1 4 4
9 4 3 9 9
8 2 1 5 4
10 1 2 3 4 6 1
6 1 1 2 5 3 2
13 1 1 2 10 3 8
11 2 11 10 7)"};
int main()
{
// Here we will store the resulting int values
std::vector<std::vector<int>> data{};
for (std::string line{}; std::getline(sourceFile, line); ) {
// Split the line into integers and add to target array
std::istringstream iss(line);
data.emplace_back(std::vector<int>(std::istream_iterator<int>(iss), {}));
}
// Now all data is in our vector of vector of int
// Show read data on screen
std::for_each(data.begin(), data.end(), [](const std::vector<int>& v){
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << "\n";});
return 0;
}
Please note. I do not have files on SO. So I used a std::istringstream as input stream. You may of course exchange it with any other std::ftream
The following program apparently shows an inconsistency when writing and reading on a std::stringstream a double set to "infinity", using std::numeric_limits<double>::infinity.
#include <string>
#include <sstream>
#include <iostream>
#include <limits>
#include <iterator>
int main() {
std::stringstream stream;
double d;
// Part 1
d=4;
stream << d << " ";
d=std::numeric_limits<double>::infinity();
stream << d << " ";
d=5;
stream << d;
stream.seekg(std::ios_base::beg);
std::string s = stream.str();
std::cout << "string : " << s << std::endl;
// Part 2
std::cout << "Extraction of data 1:" << std::endl;
stream.seekp(std::ios_base::beg);
stream >> d;
std::cout << d << std::endl;
stream >> d;
std::cout << d << std::endl;
stream >> d;
std::cout << d << std::endl;
// Part 3
std::cout << "Extraction of data 2:" << std::endl;
std::istringstream stream2(s);
std::istream_iterator<double> iter(stream2);
std::istream_iterator<double> eos;
while (iter != eos)
{
std::cout << (*iter) << std::endl;
iter++;
}
return 0;
}
See it live.
The output is
string : 4 inf 5
Extraction of data 1:
4
0
0
Extraction of data 2:
4
In part 1 a double "infinity" is written to a stringstream and the string extracted from it shows an "inf" corresponding to such an infinity.
In part 2, however, the substring "inf" is apparently extracted to 0. Moreover, the stream appears to be in a bad state, since successive extractions give again 0.
Similarly, in part 3 a istream_iterator is used to extract the doubles from the string. The iterator reaches an end-of-stream before reading "inf".
Obviously, one could easily fix the problem by extracting words instead of double and converting each of them to a "normal" double, or to std::numeric_limits<double>::infinity when a "inf" is encountered. However, this seems like reinventing the wheel, given that the standard library already contains a lot of code to extract data from a stream....
Why is there a difference when inserting and extracting a std::numeric_limits<double>::infinity?
Within the standard library, is there a possibility of extracting a double which could also be infinity, without resorting to write a function that extracts words and convert them to double?
Additional note
According to the c++ reference, std::stod and similar functions already convert "inf" to an infinite expression. Hence, it looks like std::stringstream::operator<< does not use std::stod or similar to convert a piece of string to a double.
I have a very simple test program that uses istringstreams to read in integers from a std::string. The code is:
std::map<int, int> imap;
int idx, value;
std::string str("1 2 3 4 5 6 7 8");
istringstream is(str);
while(is >> idx >> imap[idx]){
cout << idx << " " << imap[idx] << endl;
}
cout << endl;
std::map<int, int>::iterator itr;
for(itr = imap.begin(); itr != imap.end(); itr++){
cout << itr->first << " " << itr->second << endl;
}
When I run this on Solaris 10, it produces the following output:
1 2
3 4
5 6
7 8
1 2
3 4
5 6
7 8
However, when I run it under CentOS 7, I get:
1 0
3 0
5 0
7 0
1 4
3 6
5 8
7 0
4204240 2
Does anyone know why it would be different under Linux than under Solaris? It's obviously reading in the value into the map before reading into the index for the map, but I don't know why. I can make it work under Linux by changing the code slightly:
std::map<int, int> imap;
int idx, value;
std::string str("1 2 3 4 5 6 7 8");
istringstream is(str);
while(is >> idx >> value){
imap[idx] = value;
cout << idx << " " << imap[idx] << endl;
}
std::map<int, int>::iterator itr;
for(itr = imap.begin(); itr != imap.end(); itr++){
cout << itr->first << " " << itr->second << endl;
}
I know it's a valid fix, but I have people around me who want to know why it is different. We are migrating from Solaris to Linux and when things like this come up, they want to know why. I don't know why so I'm asking for guidance.
is >> idx >> imap[idx]
This expression is equivalent to
operator>>(operator>>(is, idx), imap.operator[](idx))
The evaluations of arguments to the same function are unsequenced relative to each other; either operator>>(is, idx) or imap.operator[](idx) may be evaluated first (that is, either is >> idx or imap[idx] may be evaluated first). If the latter is evaluated first, then the result is an lvalue referring to the value corresponding to the old value of idx in the map; it is this value that will be overwritten by the second read, and not the value corresponding to the new value of idx.
The modified code fixes this by ensuring that idx is read before imap[idx] is accessed.
When I try to parse whitespace seperated double values from a string, I found this curious behaviour that the string is read out in a cyclic manner.
Here's the program:
stringstream ss;
string s("1 2 3 4");
double t;
list<double> lis;
for(int j=0; j!=10; ++j){
ss << s;
ss >> t;
lis.push_back(t);
}
for(auto e : lis){
cout << e << " ";
}
Here the output:
1 2 3 41 2 3 41 2 3 41
If I append a trailing space as s= "1 2 3 4 "; I get
1 2 3 4 1 2 3 4 1 2
Now the questions:
1) If I don't know how many entries are in the string s, how do I read all into the list l?
2) which operator<< am I actually calling in ss << s;? Is it specified to read circularly?
3) Can I do the parsing in a better way?
Thanks already!
Here's the fixed code (thanks to timrau):
// declarations as before
ss << s;
while(ss >> t){
lis.push_back(t);
}
// output as before
This produces:
1 2 3 4
as desired. (Don't forget to clear your stringstream by ss.clear() before treating the next input. ;))
Another useful comment from HeywoodFloyd: One could also use boost/tokenizer to "split" the string, see this post
You can test the return value of >>.
while (ss >> t) {
lis.push_back(t);
}
It's not specified to read circularly. It's ss << s appending "1 2 3 4" to the end of the stream.
Before the 1st loop:
""
After 1st ss << s:
"1 2 3 4"
After 1st ss >> t:
" 2 3 4"
After 2nd ss << s:
" 2 3 41 2 3 4"
Then it's clear why you get 1 2 3 41 2 3 41 2 3 41 if there is no trailing space in s.
then use s.length() for strings containing unknown number of entries, if you use your approach.
Or, as suggested by timrau, just initialize your stringstream once.
stringstream ss;
string s("1 2 3 4 5 6 7 8");
ss << s;
double t;
list<double> lis;
while (ss >> t) {
lis.push_back(t);
}
for(auto e : lis){
cout << e << " ";
}
This stackoverflow post includes a boost tokenizer example. You may want to tokenize your string and iterate through it that way. That will solve the no trailing space problem timrau pointed out.
I am trying to learn stringstream and I have the following code:
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
stringstream os;
os.str("Purohit");
os << "Vipul" << endl;
cout << os.str() << endl;
}
When I compile it and run it, I get the following output:
Vipul
t
Why? Shouldn't this output Purohit Vipul?
This is because str method replaces the content of stringstresm, without placing the buffer pointer for the subsequent writes at the end of the stream. That is why when you output "Vipul\n" it writes over the "Purohit" string that you placed into the stream earlier:
Initial state
0 1 2 3 4 5 6
P u r o h i t
^
After the << write:
0 1 2 3 4 5 6
V i p u l \n t
You could call seekg to set the position before appending the "Vipul" string, but an easier fix would be to use << for both writes.