10

Assume that I have an input as follows:

N (X_1,Y_1) (X_2,Y_2) .... (X_N, Y_N)

where N, X_i and Y_i are integers.

An example:

2 (55,1) (521,7)

To read this, I can do something like this(assume all variables are defined, etc.):

fscanf(fin,"%d ",&N);
for (int i = 0; i < N; i++)
   fscanf(fin,"(%d,%d) ", &X[i], &Y[i]);

The question is, how can I do this easily using ifstream. I can get string's, and then I can get rid of nondigits and using stringstream I can get two numbers but this seems a bit cumbersome. Is there an easier, more elegant way?

Thanks

kolistivra
  • 4,229
  • 9
  • 45
  • 58

2 Answers2

8
int n, x, y;
char c;
if (is >> n)
    for (int i = 0; i < n; ++i)
        if (is >> c && c == '(' &&
            is >> x &&
            is >> c && c == ',' &&
            is >> y &&
            is >> c && c == ')')
        {
            X[i] = x;
            Y[i] = y;
        }
        else
            throw std::runtime_error("invalid inputs");

You can simplify the all-important inner if condition above to...

is >> chlit('(') >> x >> chlit(',') >> y >> chlit(')')

...with a simple support type for consuming a specific character:

struct chlit
{
    chlit(char c) : c_(c) { }
    char c_;
};

inline std::istream& operator>>(std::istream& is, chlit x)
{
    char c;
    if (is >> c && c != x.c_)
        is.setstate(std::iostream::failbit);
    return is;
}

See a complete program illustrating this on ideone here.

An old post of mine did something similar for consuming specific strings. (The above chlit could be a template, but chlit<','>() is ugly to read and write - I'd rather trust the compiler).

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • 1
    From 3 lines to 15 . . . I really like C++ but the iostreams can be hard to work with sometimes. – dreamlax Jan 17 '11 at 08:40
  • well at least it will not cause a memory overwrite if there is something unexpected in the input. – AndersK Jan 17 '11 at 08:46
  • 1
    @Anders: fscanf stops scanning on unexpected input, and returns the number of items successfully scanned. – dreamlax Jan 17 '11 at 08:48
  • @dreamlax: if you want to make it more concise, you can get some milage out of `if (is >> c1 >> X[i] >> c2 >> Y[i] >> c3 && c1 == '(' && c2 == ',' && c3 == ')')` – Tony Delroy Jan 17 '11 at 08:52
  • @Anders: input is assumed to be perfect for this question – kolistivra Jan 17 '11 at 08:53
  • Will this actually work thuogh, won't the first `is >> c` result in a space character? (I think only leading ws is consumed when reading integers) – falstro Jan 17 '11 at 08:56
  • 1
    @dreamlax: re memory overwrites, the problem is that a lot of beginners don't protect against overwriting string buffers... you can use formats like "%.20s" or "%.*s" but a lot of beginners people don't. Further, they often try to use `%s` where `%[...]` is needed. – Tony Delroy Jan 17 '11 at 08:57
  • 1
    @roe: good thing to be wary of, but no - streaming to a `char` will also skip whitespace unless you change the stream state beforehand. – Tony Delroy Jan 17 '11 at 09:03
  • @Tony: I'm with you on reading strings using `fscanf`, and also the enconcisening is good too. I think it's a good answer (+1'd). – dreamlax Jan 17 '11 at 09:19
3
cin >> N;
for (int i = 0; i < N; i++)
{
    cin.ignore(100,'(');
    cin >> X[i];
    cin.ignore(100,',');
    cin >> Y[i];
    cin.ignore(100,')');
}

It can handle whitespaces also, as it can read input like:

2  (  1  ,  3  )    (  5  ,  6  )

Demonstration at ideone: http://www.ideone.com/hO0xG

Nawaz
  • 353,942
  • 115
  • 666
  • 851