I've got a data file that is a single line consisting of a nested series of doubles eg.
[[0.127279,0.763675,0.636396],[0.254558,0.890955,0.636396],
[0.127279,0.636396,0.763675],[0.254558,0.763675,0.763675],
[0.381838,0.890955,0.763675],[0.127279,0.509117,0.890955],
[0.254558,0.636396,0.890955],[0.509117,0.890955,0.890955]]
I'd like to be able to read this into a STL vector<vector<double> > using the stream operator which is templated across the inner type of A:
vector<vector<double> > A;
FIN >> A;
I've figured out a way to do this when the vector is not nested, ie. a simple vector<T> as so:
template <class T>
istream& operator>>(istream& s, vector<T> &A){
T x;
string token; char blank;
s >> blank; // Gobble the first '['
while( getline(s, token, ',') ) {
istringstream input(token);
input >> x;
A.push_back(x);
}
s >> blank; // Gobble the last ']'
return s;
}
But I'm having problem with the istream& operator>>(istream& s, vector<vector<T> >&A) part as I can't seem to catch the inner ]'s properly. I'm sure that Boost has a way of doing this, but I'd like to see a solution with the STL for pedagogical purposes.
Note: I'm aware that overloading the stream operator for vector<T> can have far-reaching undesirable consequences and that the implementation should be wrapped up in its own class - I'm using this example above as it stands to clarify the question.
EDIT:
I'd like the method to be robust enough to handle a input array whose size (and inner array) size is not known in advance, but inferred from reading the stream.
Actually, the problem with your code that you want to use the same function for both, when T is:
vector<double>
double
But the logic which needs to read the data into vector and double is slightly different. So you cannot do that, at least not with that simple logic:
I would prefer to write two functions, to handle both cases separately. After all, even in your case, the compiler will generate two different functions for each value of T.
template <class T>
istream& operator>>(istream& s, vector<T> &A)
{
T x;
string token; char blank;
s >> blank; // Gobble the first '['
while( getline(s, token, ',') )
{
istringstream input(token);
input >> x;
A.push_back(x);
}
// s >> blank; // Gobble the last ']'
return s;
}
template <class T>
istream& operator>>(istream& s, vector<vector<T>> &A)
{
vector<T> x;
string token;
char blank;
s >> blank; // Gobble the first '['
while( getline(s, token, ']') )
{
istringstream input(token);
input >> x;
s >> blank; //read , after [...]
A.push_back(x);
x.clear();
}
s >> blank; // Gobble the last ']'
return s;
}
Test code:
int main() {
vector<vector<double>> A;
cin >> A;
for(size_t i = 0 ;i < A.size(); ++i)
{
for(size_t j = 0 ; j < A[i].size(); ++j)
cout << A[i][j] <<" ";
cout << endl;
}
return 0;
}
Input:
[[1,2,3],[4,5,6],
[7,8,9],[10,11,12],
[13,14,15],[16,17,18],
[19,20,21],[22,23,24]]
Output:
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
19 20 21
22 23 24
Online demo : http://ideone.com/iBbmw
In your particular example which is very simple.
Read the whole line into a string.
Replace all [ , ] and , with whitespace character.
Create a simple stringstream with whitespace replaced string.
Now you can have a a simple loop of
double x;
while( stringstreamp >> x )
{
}
And some special logic after reading three doubles to insert them them into a new array.
Some years later, and I was here struggling with the same problem.
Based on your contribution, I developed a modified version of the original template. This one is able to parse multidimensional arrays, even if they are spread across several lines.
template <class T>
istream& operator>>(istream& s, vector<T> &A){
while(true){
T x;
char c = s.peek();
if( c == '[' || c == ','){
s.ignore();
if(s >> x) A.push_back(x);
else throw invalid_argument("Bad, bad JS array!");
continue;
}
if( c == ']') {
s.ignore();
return s;
}
/* Ignore line-break */
s.ignore();
}
return s;
}
Hope this can be useful for someone.
Related
So, I need to store the data from the text file into 2d array. I tried using vectors. So here is the sample data from the text file:
START 13
PID 11
CORE 120
SSD 0
CORE 60
SSD 0
CORE 20
SSD 0
I want to store this data as final_vec[x][y]. This is what I tried:
void read_file(const string &fname) {
ifstream in_file(fname);
string line;
vector<string> temp_vec;
vector<vector<string>> final_vec;
while ( getline (in_file,line) )
{
stringstream ss(line);
string value;
while(ss >> value)
{
temp_vec.push_back(value);
}
final_vec.push_back(temp_vec);
}
for (int i = 0; i < final_vec.size(); i++) {
for (int j = 0; j < final_vec[i].size(); j++)
cout << final_vec[i][j] << " ";
cout << endl;
}
}
int main()
{
read_file("test.txt");
return 0;
}
I get error:
main.cpp: In function ‘void read_file(const string&)’:
main.cpp:29:29: error: variable ‘std::stringstream ss’ has initializer but incomplete type
stringstream ss(line);
I am not sure if I am on the right track.
IMHO, a better solution is to model each line as a record, with a struct or class:
struct Record
{
std::string label;
int number;
friend std::istream& operator>>(std::istream& input, Record& r);
};
std::istream& operator>>(std::istream& input, Record& r)
{
input >> r.label;
input >> r.number;
return input;
}
The overloaded operator>> makes the input loop a lot simpler:
std::vector<Record> database;
Record r;
while (infile >> r)
{
database.push_back(r);
}
Rather than have a 2d vector of two different types, the above code uses a 1D vector of structures.
I have a file which first tells me how many points will I be reading on the following line. So, for example my file look like this:
7
a,b c,d e,f g,h, i,j k,l m,n
So I know the following line after 7 is 7 pairs of integers separated by a comma and each pair separated by a blank space.
What I want: To have a vector of 7 Point elements.
I have a class called Point:
class Point {
public:
int x;
int y;
bool operator==(const Point q){
return (q.x == this->x && q.y == this->y);
}
};
So when I read this file I'd like to have a vector V where:
V[0].x = a
V[0].y = b
V[1].x = c
V[1].y = d
and so on.
I can read the 7 fine, but how do I read each of the 7 pairs of integers individually? I need this because I'm going to store (a,b) (c,d)... in a vector.
Is not only 2 points. The first line of the file tells me how many points I'm going to store.
They're not read from standard input.
They're read from a file.
I tried using sscanf but I think that's only for when you have multiple lines with this info and I'd like to not have to modify my format.
This is what I have so far:
void process_file(string filename){
ifstream thracklefile;
string line;
int set_size;
thracklefile.open(filename);
getline(thracklefile,line); //store set size.
set_size = stoi(line);
//Store points in following line
points.clear();
points.resize(set_size);
getline(thracklefile,line); //store the points.
}
I do not want to ignore commas, each comma is part of the information I want to store for each Point.
I think most of the discussion in the comments is about semantics. It is recommended you "ignore" the commas but you can't do that as they are in the file. Perhaps a better term is "discard". The word "ignore" is use since there is a C++ iostream function ignore.
There are many ways to handle this. One option is to override the stream insertion/extraction operators:
class Point {
public:
int x;
int y;
// Don't really need this as members are public, but
// in case you change that in the future....
friend istream& operator>>(istream& in, Point& p);
friend ostream& operator<<(ostream& out, const Point& p);
};
istream& operator>>(istream& in, Point& p)
{
char separator;
// Try to read <int><char><int>
in >> p.x >> separator >> p.y;
// The stream may be in an error state here. That
// is ok. Let the caller handle that
// Also note that we discard (ignore) "separator"
return in;
}
ostream& operator<<(ostream& out, const Point& p)
{
out << p.x << ',' << p.y;
return out;
}
int main() {
int num_points;
std::cin >> num_points;
Point p;
for (int i = 0; i < num_points; i++) {
if (!(std::cin >> p)) {
// There was an error
std::cout << "File format error!" << std::endl;
break;
}
std::cout << p << std::endl;
}
return 0;
}
The example uses cin but any stream should work, including ifstream.
I am C++ noob, I have a text file with 4 rows and 3 columns, where each row corresponds to a sensor signal. How do I load each row to a separate vector<float>?
(0.165334,0) (0.166524,-0.0136064) (-0.144899,0.0207161)
(0.205171,0) (0.205084,-0.0139042) (-0.205263,0.0262445)
(0.216684,0) (0.215388,-0.0131107) (-0.193696,0.0251303)
(0.220137,0) (0.218849,-0.0135667) (-0.194153,0.025175)
This is what I have so far, but this code loads data as string. I want to load my final data as vector<vector<float>>?
vector<vector<string> > input;
ifstream fileFFT(Filename.c_str());
string line;
while(getline(fileFFT, line)){
if(line.empty()){
continue;
}
stringstream row(line);
vector<string> values((istream_iterator<string>(row)),(istream_iterator<string>())); //end
input.push_back(values);
}
Here's something to get you started:
class Point
{
public:
double x;
double y;
friend std::istream& operator>>(std::istream& input, Point& p);
};
std::istream& operator>>(std::istream& input, Point& p)
{
char c;
input >> c; // Read open parenthesis
input >> p.x;
input >> c; // Read comma
input >> p.y;
input >> c; // Read closing parenthesis
return input;
};
//...
std::string row_text;
std::vector<std::vector<Point>> matrix;
while (std::getline(my_file, row_text))
{
std::vector<Point> row;
std::istringstream(row_text);
Point p;
while (row_text >> p)
{
row.push_back(p);
}
matrix.push_back(row);
}
I've created a Point class to represent the pair of floating point numbers.
I also overloaded operator>> to make reading a Point easier.
The loop reads one record or text line, then creates a vector of Point from the text line.
The record or row is then appended to the matrix.
You have half the answer already - use std::getline() to read each line, and then use std::(i)stringstream to process each line.
Now, what you are missing is the other half - parsing each line. And since you already know how to use std::istream_iterator, I would do something like this:
typedef std::pair<float, float> SensorValue;
typedef std::vector<SensorValue> SensorValues;
std::istream& operator>>(std::istream &in, SensorValue &out)
{
float f1, f2;
char ch1, ch2, ch3;
if (in >> ch1 >> f1 >> ch2 >> f2 >> ch3)
{
if ((ch1 == '(') && (ch2 == ',') && (ch3 == ')'))
out = std::make_pair(f1, f2);
else
in.setstate(std::ios_base::failbit);
}
return in;
}
...
std::vector<SensorValues> input;
std::ifstream fileFFT(Filename.c_str());
std::string line;
while (std::getline(fileFFT, line))
{
if (line.empty())
continue;
std::istringstream row(line);
SensorValues values;
std::copy(std::istream_iterator<SensorValue>(row), std::istream_iterator<SensorValue>(), std::back_inserter(values));
input.push_back(values);
}
So my problem is, when i read the file the "ki" , "kivel" and the "meddig" variables are good, but the "mettol" variable seems like it is disappeared.
struct Haboru {
string ki;
string kivel;
int mettol;
int meddig;
};
int main()
{
Haboru haboruk[10];
int k = 0;
ifstream haboru;
haboru.open("haboruk.txt");
// The rows are in "haboruk.txt" like these:
// Xhosa Zulu 1696 1736
// Zulu Ndebele 1752 1782
// Zulu Sotho 1756 1772
while(!haboru.eof())
{
haboru >> haboruk[k].ki >> haboruk[k].kivel >> haboruk[k].mettol >> haboruk[k].meddig;
k++;
}
}
The output is this:
Using !file.eof() as a condition to extract is not correct. You have to perform the extraction, and then check if the file is valid. But even using !file.eof() afterwards is still not correct:
Let's make this simpler by creating an inserter for a Haboru object:
std::istream& operator>>(std::istream& is, Haboru& haboruk)
{
if (!is.good())
return is;
is >> haboruk.ki;
is >> haboruk.kivel;
is >> haboruk.mettol >> haboruk.meddig;
return is;
}
Then you can create your vector (or std::array C++11) and use the inserter for each element:
std::vector<Haboru> haboruks;
Haboru haboruk;
while (haboru >> haboruk)
{
haboruks.push_back(haboruk);
}
Or...
std::vector<Haboru> haboruks((std::istream_iterator<Haboru>(haboru)),
std::istream_iterator<Haboru>());
In a C++ program I'm trying to process user input which consists of integer operands interspersed with operators (+ - / *). I have the luxury of requiring users to put whitespace(s) before and after each operator. My approach is to assume that anything that's not an int is an operator. So as soon as there's a non eof error on the stream, I call cin.clear() and read the next value into a string.
#include <iostream>
#include <string>
//in some other .cpp i have these functions defined
void process_operand(int);
void process_operator(string);
using namespace std;
int main()
{
int oprnd;
string oprtr;
for (;; )
{
while ( cin >> oprnd)
process_operand(oprnd);
if (cin.eof())
break;
cin.clear();
cin >> oprtr;
process_operator(oprtr);
}
}
This works fine for / and * operators but not for + - operators. The reason is that operator>> eats away the + or - before reporting the error and doesn't put it back on the stream. So I get an invalid token read into oprtr.
Ex: 5 1 * 2 4 6 * / works fine
5 1 + 2
^ ---> 2 becomes the oprnd here.
What would be a good C++ way of dealing with this problem?
Read in std::strings and convert them using boost::lexical_cast<> or its equivalent.
int main()
{
string token;
while ( cin >> token) {
try {
process_operand(boost::lexical_cast<int>(token));
} catch (std::bad_cast& e) {
process_operator(token);
}
}
}
Postscript: If you are allergic to Boost, you can use this implementation of lexical_cast:
template <class T, class U>
T lexical_cast(const U& u) {
T t;
std::stringstream s;
s << u;
s >> t;
if( !s )
throw std::bad_cast();
if( s.get() != std::stringstream::traits_type::eof() )
throw std::bad_cast();
return t;
}
I think >> thinks you are starting another integer with +/-. Then gets mad when you don't follow with digits.
As #Robᵩ said, read a string and cast. I would only offer another choice from the standard library:
int stoi(const string& str, size_t *idx = 0, int base = 10);
This throws invalid_argument if no conversion could be performed or out_of_range if the converted value is outside the range of representable values for the return type.
This is from The Standard.