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.
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
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I've got a text file which contains several lines of integers, each integer is separated by a space, I want to read these integers into an array, where each new line is the first dimension of the array, and every integer on that line is saved into the second dimension.
My text file looks something like this:
0 1 2 3 4 5 6 7 8 9
9 0 1 2 3 4 5 6 7 8
8 9 0 1 2 3 4 5 6 7
7 8 9 0 1 2 3 4 5 6
6 7 8 9 0 1 2 3 4 5
5 6 7 8 9 0 1 2 3 4
4 5 6 7 8 9 0 1 2 3
3 4 5 6 7 8 9 0 1 2
2 3 4 5 6 7 8 9 0 1
So here's what i tried so far, but it looks like a mess
string array[30][30]; //row, column
ifstream myfile("numbers.txt");
int row = 0;
int col = 0;
while(!myfile.eof())
{
//Extract columns
while(getline(myfile, array[row][col]),!'\n')
{
getline(myfile,array[row][col],' ');
col++;
}
//Extract rows
// getline(myfile,array[row][col],'\n');
// row++;
cout<< row << '\t' << array[row][col] << "\n";
}
while(!myfile.eof()) is rarely a good idea. When you've read your last line, that condition will still evaluate to true. eof() will only be set once you've tried to read beyond the last character in the file. Also, string array[30][30] is a hardcoded 30x30 C style array that doesn't fit your data. Instead, use the C++ container std::vector (that can be nested in as many dimensions as you'd like) to dynamically add numbers.
Assuming that you don't have blank lines in numbers.txt you could do like this:
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <stdexcept>
std::vector<std::vector<int>> get_2d_array_of_ints_from_stream(std::istream& is) {
std::vector<std::vector<int>> return_value;
std::string line;
while(std::getline(is, line)) { // if this fails, EOF was found or there was an error
std::istringstream iss(line); // put the line in a stringstream to extract numbers
int value; // temporary used for extraction
std::vector<int> line_values; // all values on this line
while(iss >> value) // extract like when reading an int from std::cin
line_values.push_back(value); // put the value in the 1D (line) vector
// check that all lines have the same amount of numbers
if(return_value.size() && return_value[0].size()!=line_values.size())
throw std::runtime_error("file format error");
return_value.emplace_back(std::move(line_values)); // move this line's vector<int>
// to the result_value
}
return return_value;
}
int main() {
if(std::ifstream is{"numbers.txt"}; is) {
try {
// auto arr2d = get_2d_array_of_ints_from_stream(is);
// would be the same as:
std::vector<std::vector<int>> arr2d = get_2d_array_of_ints_from_stream(is);
std::cout << "Got a " << arr2d[0].size() << "x" << arr2d.size() << " array\n";
for(const std::vector<int>& line_values : arr2d) {
for(int value : line_values) {
std::cout << " " << value;
}
std::cout << "\n";
}
std::cout << "--\n";
// or you can use the subscript style of arrays
for(size_t y = 0; y < arr2d.size(); ++y) {
for(size_t x = 0; x < arr2d[y].size(); ++x) {
std::cout << " " << arr2d[y][x];
}
std::cout << "\n";
}
} catch(const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << "\n";
}
}
}
How could you move array characters?????????
Here is some basic code with high-level comments. It is not exactly as you desire. But since you have provided some code it is nearly there.
After reading the comments and understanding what is happening, it should be relatively straightforward to modify the below code to your requirements:
#include <iostream>
void printArray(int gameboard[5][5]){
std::cout << "This is what the gameboard looks like now:" << std::endl;
for ( int i = 0; i < 5; i++ ) {
for ( int j = 0; j < 5; j++ ) {
std::cout << gameboard[i][j] << ' ';
}
std::cout << std::endl;
}
}
int main() {
// Declare array and print what it looks like
int gameboard[5][5] = { {1,2,3,4,5}, {1,2,3,4,5}, {1,2,3,4,5}, {1,2,3,4,5}, {1,2,3,4,5}};
printArray(gameboard);
// Get input for which coordinates the user wants to swap
int row1, column1, row2, column2;
std::cout << "Please enter the coordinates of the first piece:" << std::endl;
std::cout << "Row:";
std::cin >> row1;
std::cout << "Column:";
std::cin >> column1;
std::cout << "Please enter the coordinates of the second piece:" << std::endl;
std::cout << "Row:";
std::cin >> row2;
std::cout << "Column:";
std::cin >> column2;
// Swap values at provided coordinates by using a temp variable
int temp = gameboard[row1][column1];
gameboard[row1][column1] = gameboard[row2][column2];
gameboard[row2][column2] = temp;
printArray(gameboard);
return 0;
}
Example Usage:
This is what the gameboard looks like now:
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Please enter the coordinates of the first piece:
Row: 0
Column: 0
Please enter the coordinates of the second piece:
Row: 4
Column: 4
This is what the gameboard looks like now:
5 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 1
Tasks TO-DO for you:
Change printArray to allow arrays of varying sizes not just 5 x 5.
Ensure user input for row, column and value are numbers.
Ensure user input row, and column values are within the bounds of the array.
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.
For my map creation algorithm, the user inputs numbers such as this in a data file:
0, 3, 0, 0
14, 2, 26, 5
The numbers represent the id of a certain texture of a tile in order to generate cell/world data. I've already made the part that takes away the commas to make it look like this:
0 3 0 0
14 2 26 5
The problem I'm having is that I want to push the certain numbers into a stringstream so they can be parsed and given the correct texture. The certain numbers will be 1 space away from each other so it's easy to push it into the stringstream, but how would I jump from each number to another in order to push it into the stringstream in the same order?
Thanks!
Maybe something like this:
std::string snumbers = ...; // your input string with no commas
std::stringstream sstream(snumbers);
std::vector<std::string> items;
std::string item;
while(sstream >> item) items.push_back(item);
const size_t N = items.size() / 2; // this is the number of pairs you have
std::cout << "pair 0: " << items[0] << ", " << items[N] << std::endl;
std::cout << "pair 1: " << items[1] << ", " << items[1+N] << std::endl;
...