The order is known, since each line follows the variables from a
struct. The number of lines may vary, but, for now, let's assume it is
also known. Also for the sake of example, let's only consider this
order, and these types:
int, double, double, int
If the number and order of the fields is known, then you can simply read with >>
or getline
using both the ','
or '\n'
delimiter as required. While it is much wiser to use line-oriented input to read an entire line and then stringstream
to parse the fields, there is no reason you can't do the same thing utilizing only fstream
as you have indicated is your goal. It's not as elegant of a solution, but a valid one nonetheless.
Using the >>
Operator
Your data has 4-fields, the first 3 are delimited by a comma
, the final delimited by the newline
. You can simply loop continually and read using the >>
operator and test for fail()
or eof()
after each read, e.g.
#include <iostream>
#include <fstream>
#define NFIELD 4
#define MAXW 128
int main (int argc, char **argv) {
int a, d;
double b, c;
char comma;
std::fstream f (argv[1]);
if (!f.is_open()) {
std::cerr << "error: file open failed " << argv[1] << ".\n";
return 1;
}
for (;;) { /* loop continually */
f >> a >> comma >> b >> comma >> c >> comma >> d;
if (f.fail() || f.eof())
break;
std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
f.ignore (MAXW, '\n');
}
f.close();
}
Keeping a simple field counter n
, you can use a simple switch
statement based on the field number to read the correct value into the corresponding variable, and when all fields are read output (or otherwise store) all 4 values that make up your struct. (obviously you can fill each member at the time they are read as well). Nothing special is required, e.g.
#include <iostream>
#include <fstream>
#define NFIELD 4
int main (int argc, char **argv) {
int a, d, n = 0;
double b, c;
char comma;
std::fstream f (argv[1]);
if (!f.is_open()) {
std::cerr << "error: file open failed " << argv[1] << ".\n";
return 1;
}
for (;;) { /* loop continually */
switch (n) { /* coordinate read based on field number */
case 0: f >> a >> comma; if (f.eof()) goto done; break;
case 1: f >> b >> comma; if (f.eof()) goto done; break;
case 2: f >> c >> comma; if (f.eof()) goto done; break;
case 3: f >> d; if (f.eof()) goto done; break;
}
if (++n == NFIELD) { /* if all fields read */
std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
n = 0; /* reset field number */
}
}
done:;
f.close();
}
Example Input File
Using your provided sample input.
$ cat dat/mixed.csv
1,1.1,11.1,11
2,2.2,22.2,22
3,3.3,33.3,33
Example Use/Output
You obtain your desired output by simply doubling each field on output:
$ ./bin/csv_mixed_read dat/mixed.csv
2,2.2,22.2,22
4,4.4,44.4,44
6,6.6,66.6,66
(the output for both above is the same)
Using getline
Delimited by ','
and '\n'
You can use a slight variation on the logic to employ getline
. Here, you read the first 3 fields with f.getline(buf, MAXC, ',')
, and when the 3rd field is found, you read the final field with f.getline(buf, MAXC)
. For example,
#include <iostream>
#include <fstream>
#define NFIELD 4
#define MAXC 128
int main (int argc, char **argv) {
int a = 0, d = 0, n = 0;
double b = 0.0, c = 0.0;
char buf[MAXC];
std::fstream f (argv[1]);
if (!f.is_open()) {
std::cerr << "error: file open failed " << argv[1] << ".\n";
return 1;
}
while (f.getline(buf, MAXC, ',')) { /* read each field */
switch (n) { /* coordinate read based on field number */
case 0: a = std::stoi (buf); break;
case 1: b = std::stod (buf); break;
case 2: c = std::stod (buf);
if (!f.getline(buf, MAXC)) /* read d with '\n' delimiter */
goto done;
d = std::stoi (buf);
break;
}
if (++n == NFIELD - 1) { /* if all fields read */
std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
n = 0; /* reset field number */
}
}
done:;
f.close();
}
(note: unlike using the >>
operator, when using getline
as above, there can be no whitespace following each comma
.)
Example Use/Output
The output is the same.
$ ./bin/csv_mixed_read2 dat/mixed.csv
2,2.2,22.2,22
4,4.4,44.4,44
6,6.6,66.6,66
Regardless whether you use something like the examples above, or stringstream
, you will have to know the number and order of the fields. Whether you use a loop and if..else if..else
or switch
the logic is the same. You need some way of coordinating your read with the correct field. Keeping a simple field counter is about as simple as anything else. Look things over and let me know if you have further questions.