Reputation: 127
I've been trying to read a file in C/C++
and was able to do so for C
but am facing some problems with C++
.
Background: The file I'm trying to read has the following structure
i = %3d; j = %3d; a = %3d; b = %3d;
i = %3d; j = %3d; a = %3d; b = %3d;
...
The code I have which works in C
is the following:
#include <stdio.h>
#include <fstream>
#define MAXSIZE 32
int main()
{
FILE* inputfile = fopen("temp.txt","r");
int i,j,a,b;
while (!feof(inputfile))
{
fscanf(inputfile,
"i = %3d; "
"j = %3d; "
"a = %3d; "
"b = %3d;\n",
&i, &j, &a, &b);
printf("%3d %3d %3d %3d\n", i,j,a,b);
}
fclose(inputfile);
return 0;
}
When trying to write this program in C++
I've tried:
int main()
{
ifstream inputfile( "temp.txt" ,ios::in);
int i,j,a,b;
while (!std::cin.eof())
{
inputfile >> "i = " >> i >> "; j = " >> j >> "; a = " >> a >> "; b = " >> b >> ";\n";
printf("%3d %3d %3d %3d\n", i,j,a,b);
}
return 0;
}
But I'm not too sure how I should go about formatting
inputfile >> "i = " >> i >> "; j = " >> j >> "; a = " >> a >> "; b = " >> b >> ";\n";
Any help will be appreciated. Thanks!
Edit: The problem I'm facing is that the pernultimate line is not formatted in the right way. Because my file is in the form stated in the question, I cannot write inputfile >> i >> j >> a >> b;
.
Upvotes: 0
Views: 239
Reputation: 490488
Assuming you can get away with just ignoring the i =
, j =
and so on, and just need to read the numbers, one possibility would be to create a ctype facet that classifies those characters as white space. Imbue the stream with a locale that includes that facet, and just read your numbers:
#include <iostream>
#include <algorithm>
#include <locale>
#include <vector>
#include <sstream>
struct digits_only: std::ctype<char> {
digits_only(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::space);
std::fill(&rc['0'], &rc['9'], std::ctype_base::digit);
return &rc[0];
}
};
class record {
int i, j, a, b;
public:
friend std::istream &operator>>(std::istream &is, record &r) {
return is >> r.i >> r.j >> r.a >> r.b;
}
friend std::ostream &operator<<(std::ostream &os, record const &r) {
return os << r.i << "\t" << r.j << "\t" << r.a << "\t" << r.b;
}
};
int main() {
std::stringstream in("i=1 j = 2 a = 10 b = 20\ni = 3 j = 4 a = 30 b = 40");
std::locale numbers(std::locale(), new digits_only);
in.imbue(numbers);
std::vector<record> records{ std::istream_iterator<record>(in),
std::istream_iterator<record>() };
std::cout << "i\tj\ta\tb\n";
for (auto const & r : records)
std::cout << r << "\n";
}
As to whether this is better or worse than just using scanf
, I'd say it's open to some question. It's clearly longer overall. OTOH, a lot of the extra length in the code above is from my deciding to wrap i
, j
, a
and b
into a structure of their own. Though it's only a guess, my guess is that you probably want to do that in any case.
In exchange for some extra length, you get type safety, and if you have several different parts of your code that need to read records like this, a lot more convenience.
Contrary to Ben Voigt's claim, actual testing shows that I/O using iostreams can be completely competitive with C's stdio functions (and faster in some cases). I haven't tested this particular case enough to be sure whether it's faster, slower, or about the same, but I've done enough other testing to say with reasonable certainty that it's far from a foregone conclusion that C's stdio functions will be substantially faster.
Bottom line: it's possible that continuing to use scanf
is a good choice--but then again, it may not be either.
Upvotes: 1
Reputation: 283793
Just use the original code. It's better in every way:
However, I will offer one improvement:
while (4 == fscanf(inputfile,
"i = %3d; "
"j = %3d; "
"a = %3d; "
"b = %3d;\n",
&i, &j, &a, &b))
{
printf("%3d %3d %3d %3d\n", i,j,a,b);
}
if (!feof(inputfile)) {
// read failed and the reason wasn't hitting the end of the file
// maybe you've been keeping track of line numbers and
// can report where the invalid input was found
}
Testing EOF before trying to read is too early. The EOF flag only gets set after a read hits the end of the file. If the last successful read actually found the '\n'
, then EOF won't be set. The iostreams code had the same bug, in addition to the problem dealing with intervening text.
Nota Bene: You'll never notice the speed difference for user input, or even for output to a terminal. But for file I/O (including redirected stdin or stdout), the stdio.h functions beat iostreams by a lot.
Upvotes: 3