11

What is the best option if I want to "upgrade" old C-code to newer C++ when reading a file with a semicolon delimiter:

/* reading in from file C-like: */
fscanf(tFile, "%d", &mypost.nr); /*delimiter ; */
fscanf(tFile, " ;%[^;];", mypost.aftername);/* delimiter ; */
fscanf(tFile, " %[^;]", mypost.forename);   /*delimiter ; */
fscanf(tFile, " ;%[^;];", mypost.dept);/*delimiter ; */
fscanf(tFile, " %[^;];", mypost.position);/* delimiter ; */
fscanf(tFile, "%d", &mypost.nr2);

//eqivalent best C++ method achieving the same thing?
Péter Török
  • 114,404
  • 31
  • 268
  • 329
Chris_45
  • 8,769
  • 16
  • 62
  • 73
  • I wonder how strict fscanf is? If you read all characters up to a semicolon on the line with forename, how can the next line read a space before the semicolon? - Also, `fscanf` probably wouldn't that bad if you could also specify the maximum number of characters to read somewhere. – UncleBens Mar 29 '10 at 15:49

3 Answers3

11

You could overload the right-shift operator on istream for your struct, so:

std::istream& operator>>(std::istream& is, mypost_struct& mps) {
    is >> mps.nr;
    is.ignore(1, ';');
    is.getline(mps.forename, 255, ';');
    is.getline(mps.aftername, 255, ';');
    is >> mps.dept;
    is.ignore(1, ';');
    is >> mps.position;
    is.ignore(1, ';');
    is >> mps.nr2;

    return is;
}

Subsequently, input is as simple as is >> mypost;, where is is the file that you have opened.

Edit: @UncleBens Thanks for pointing this out, I had forgotten to take spaces in account. I have updated the answer, assuming that forename and aftername are likely to contain spaces. And there was this rather embarrasing bit about the delimiters being double-quoted...

I just checked it using a struct definition as under:

struct mypost_struct {
    int nr;
    char forename[255], aftername[255];
    int dept, position, nr2;
};

.. and the result was as expected.

susmits
  • 2,210
  • 2
  • 23
  • 27
  • I wonder what all the upvotes are for. The idea of providing overloaded `>>` is fine but the implementation (**how** to read delimited input) is completely broken. `ignore` doesn't have this form, and even if you fix it this won't change the behavior of `istream.operator>>` and use ";" as a delimiter. It would sort of work if the input is also delimited with whitespace and none of the strings contain more than one word. – UncleBens Mar 29 '10 at 14:56
3

As @susmits says, but you can also use the returned stream as a conditional, like:

if (is >> mps.nr && is.ignore(1, ";") && is >> mps.aftername && ...) {
   // all is well ...
} else {
   // bad input format
}

or even:

if (is >> mps.nr >> ignore(";") >> mps.aftername >> ...) {
    // all is well ...
} else {
    // bad input format
}
Rasmus Kaj
  • 4,224
  • 1
  • 20
  • 23
1

What is the best option if I want to "upgrade" old C-code to newer C++...?

IMHO, the best way to do this would be to read the file line-by-line and use regular expressions for parsing.

Agnel Kurian
  • 57,975
  • 43
  • 146
  • 217