Reputation: 87
I have trouble to read in an input from user and convert them into matrix for calculation. For example, with the input = {1 2 3 / 4 5 6}
, the program should read in the matrix in the form of
1 2 3
4 5 6
which have 3 cols and 2 rows. What i got so far which does not seem to work:
input.replace(input.begin(), input.end(), '/', ' ');
stringstream ss(input);
string token;
while (getline(ss, token, ' '))
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
int tok = atoi(token.c_str());
(*matrix).setElement(i, j, tok);
}
}
}
So what I'm trying to do is to break the input into token and store them into the matrix using the setElement function which take the number of row, column and the variable that user want to store. What wrong with this code is that the variable of tok
doesnt seem to change and keep stuck in 0
. Assuming that row and col are knows.
Thanks so much for any help.
Upvotes: 0
Views: 4892
Reputation: 20730
Although many simple ways exist to solve the specific problem (and other answer have various good suggestions) let me try to give a more general view of the problem of "formatted input".
There are essentially three kind of problems, here:
this 3 things are not fully independent and the last is needed to know how to store elements (how do you size the matrix?)
Finally there is a 4th problem (that is spread all other the other 3): what to do if the input is "wrong".
These kind of problem are typically afforded in two opposite ways:
Point 2. requires good string manipulations, but also requires the ability to know how the input is long (what happens if one of the separating spaces is a new-line? the idea the everything is got with a getline
fails in those cases)
Point 1 requires a Matrix
class that is capable to grow as you read or a temporary dynamic structure (like and std
container) in which you can place what you read before sending it into the appropriate place.
Since I don't know how your matrix works, let me keep a temporary vector and counters to store lines.
#include <vector>
#include <iostream>
#include <cassert>
class readmatrix
{
std::vector<int> data; //storage
size_t rows, cols; //the counted rows and columns
size_t col; //the counting cols in a current row
Matrix& mtx; //refer to the matrix that has to be read
public:
// just keep the reference to the destination
readmatrix(Matrix& m) :data(), rows(), cols(), cols(), mtx(m)
{}
// make this class a istream-->istream functor and let it be usable as a stream
// manipulator: input >> readmatrix(yourmatrix)
std::istream& operator()(std::istream& s)
{
if(s) //if we can read
{
char c=0:
s >> c; //trim spaces and get a char.
if(c!='{') //not an open brace
{ s.setstate(s.failbit); return s; } //report the format failure
while(s) //loop on rows (will break when the '}' will be found)
{
col=0;
while(s) //loop on cols (will break when the '/' or '}' will be found)
{
c=0; s >> c;
if(c=='/' || c=='}') //row finished?
{
if(!cols) cols=col; //got first row length
else if(cols != col) //it appears rows have different length
{ s.setstate(s.failbit); return s; } //report the format failure
if(c!='/') s.unget(); //push the char back for later
break; //row finished
}
s.unget(); //pushthe "not /" char back
int x; s >> x; //get an integer
if(!s) return s; //failed to read an integer!
++col; data.push_back(x); //save the read data
}
++rows; //got an entire row
c=0; s >> c;
if(c == '}') break; //finished the rows
else s.unget(); //push back the char: next row begin
}
}
//now, if read was successful,
// we can dispatch the data into the final destination
if(s)
{
mtx.setsize(rows,cols); // I assume you can set the matrix size this way
auto it = data.begin(); //will scan the inner vector
for(size_t r=0; r<rows; ++r) for(size_t c=0; c<cols; ++c, ++it)
mtx(r,c) = *it; //place the data
assert(it == data.end()); //this must be true if counting have gone right
}
return s;
}
};
Now you can read the matrix as
input >> readmatrix(matrix);
You will notice at this point that there are certain recurring patterns in the code: this is typical in one-pass parses, and those patterns can be grouped to form sub-parsers. If you do it generically you -in fact- will rewrite boost::spirit
.
Of course some adaption can be done depending on how your matrix works (has it fixed sizes??), or what to do if rows sizes don't match (partial column filling ??)
You can even add a formatted input operator like
std::istream& operator>>(std::istream& s, Matrix& m)
{ return s >> readmatrix(m); }
so that you can just do
input >> matrix;
Upvotes: 1
Reputation: 184
If you know the size of the matrix on forehand you actually don't need getline, you should read int by int. (untested code)
input.replace(input.begin(), input.end(), '/', '\n');
stringstream ss(input);
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
int tok;
ss >> tok;
(*matrix).setElement(i, j, tok);
}
}
Upvotes: 0
Reputation: 390
You are trying to operate on each cell of the matrix for each char read in the input! You have to take one char for each cell, not multiple.
Splitting a string in tokens can be done by using the following function. Please don't be shocked that the following code isn't runnable, this is due to the missing matrix class.
Try the following:
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
using namespace std;
void split(const string& str, char delimiter, vector<string>& result) {
string::size_type i = 0;
string::size_type delimOcc = str.find(delimiter);
while (delimOcc != string::npos) {
result.push_back(str.substr(i, delimOcc-i));
i = ++delimOcc;
delimOcc = str.find(delimiter, delimOcc);
if (delimOcc == string::npos) {
result.push_back(str.substr(i, str.length()));
}
}
}
int main()
{
std::string input = "1 2 3 / 4 5 6";
vector<string> rows;
split(input, '/', rows);
for(int i = 0; i < rows.size(); i++) {
vector<string> cols;
split(rows[i], ' ', cols);
for(int j = 0; j < cols.size(); j++) {
if(cols[j][0] != '\0'){
int tok = stoi(cols[j]);
(*matrix).setElement(i, j, tok);
cout << tok << " - " << i << " - " << j << endl;
}
else {
if(j == 0) j--;
}
}
}
return 0;
}
Upvotes: 0