使用ifstream作为fscanf函数

10

假设我有以下输入:

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

其中N、X_i和Y_i都是整数。

举个例子:

2 (55,1) (521,7)

为了阅读这段代码,我可以像这样做(假设所有变量都已定义等):

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

问题是,我如何使用ifstream轻松完成这个任务。我可以获得字符串,然后可以去除非数字字符,使用stringstream可以获取两个数字,但这似乎有点繁琐。是否有更简单、更优雅的方法?

谢谢


为什么不直接使用已经在使用的scanf呢?还是你想学习所以才问的? - falstro
我问这个问题只是因为我想学习 =) - kolistivra
3
上一次我建议在C++程序中使用C风格的IO时,所有在SO上的C++精英们都对我进行了攻击。 - dreamlax
2个回答

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");

您可以将上面非常重要的内部if条件简化为...
is >> chlit('(') >> x >> chlit(',') >> y >> chlit(')')

...通过一个简单的支持类型来消耗特定字符:

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;
}

点击这里可以查看一个完整的程序示例。

我以前的一篇文章介绍了类似于消耗特定字符串的操作。(上面的chlit可能是一个模板,但chlit<','>()读起来和写起来都很丑陋 - 我更愿意相信编译器)。


1
从3行到15行……我真的很喜欢C++,但有时iostreams很难处理。 - dreamlax
至少,如果输入中有意外情况,它不会导致内存覆盖。 - AndersK
1
@Anders:fscanf在遇到意外输入时会停止扫描,并返回成功扫描的项目数。 - dreamlax
1
@dreamlax:关于内存覆盖的问题,很多初学者没有保护字符串缓冲区,这是个问题...你可以使用格式化字符串如"%.20s"或"%.*s",但很多初学者不会。此外,他们经常尝试使用%s而需要使用%[...] - Tony Delroy
1
@roe:要警惕的好事,但不是 - 将流式传输到“char”也会跳过空格,除非您事先更改流状态。 - Tony Delroy
显示剩余4条评论

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,')');
}

它也可以处理空格,因为它可以读取像这样的输入:
2  (  1  ,  3  )    (  5  ,  6  )

演示在ideone上:http://www.ideone.com/hO0xG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接