用流读取复杂的结构

时间:2011-08-06 16:32:59

标签: c++ iostream

考虑我有以下“浮动对”对象输入格式:(第一个变量,第二个变量),例如(1.0,15.6)。

阅读此类结构的最佳方式是什么?在CI中会使用scanf(“(%f,%f)”,&amp; var1,&amp; var2) - 非常好,不是吗?(​​是的,我知道它不提供类型安全等等)< / p>

但我知道只有一种方法可以使用C ++流:

float var1, var2;
char tmp;
cin >> tmp;
cin >> var1;
cin >> tmp;
cin >> var2;
cin >> tmp;

看起来很难看,它只是一对花车。那么,有一种优雅的方式吗?像

cin >> "(" >> var1 >> ", " >> var2 >> ")";

3 个答案:

答案 0 :(得分:2)

可能Boost.Spirit是你能找到的最接近的东西。这是an example如何使用它来实现你想要的。

答案 1 :(得分:1)

我这样做的方法是创建一个代表一对浮点数的类:

class FloatPair {
public:
    FloatPair() : a(), b() { }

    float a, b;
};

重载operator>>以使用流:

istream& operator>>(istream& rhs, FloatPair& out) {
    rhs.ignore(256, '(');
    rhs >> out.a;
    rhs.ignore(256, ' ');
    rhs >> out.b;
    rhs.ignore(256, ')');

    return rhs;
}

所以你可以直接在FloatPair

中流式传输
FloatPair fp1, fp2;
cin >> fp1 >> fp2;

这有点脆弱,因为如果输入中的格式(float, float)非常偏离,它就会中断。

答案 2 :(得分:1)

我会写一个输入操作符:
偷走@Seth Carnegie的FloatPair。

因此输入看起来很正常:

FloatPair c;
std::cin >> c;

我会让它看起来像这样。

std::istream& operator>>(std::istream& stream, FloatPair& out)
{
    return stream >> I('(') >> out.a >> I(',') >> out.b >> I(')');
}

然后我有一个这样的忽略对象。
如果你愿意,可以很容易地进行模板化。为了使代码更容易,我称之为I

template<typename T>
struct ignore
{
    T ignoreItem;
    ignore(T const& x): ignoreItem(x) {}
};
template<typename T>
ignore<T> I(T const& x) { return ignore<T>(x);}  // This is where I in the above code comes from.

然后输入运算符&gt;&gt;因为忽略看起来像这样。

std::istream& operator>>(std::istream& stream, ignore<T> const& test)
{
    T   next;
    if ((stream >> next) && (test.ignoreItem != next))       // if the stream already failed
    {    stream.setstate(std::ios::badbit);                 // then don't change anything
    }                                                       // as it may confuse people
    return stream;
}

字符串的特化。处理运算符&gt;&gt;的事实字符串只读一个字 注意:在scanf()中,空格与1个或多个空格匹配。因此,如果输入字符串中有空格,则遵循相同的规则。

template<>
struct ignore<std::string>
{
    std::vector<std::string> ignoreItemVector;
    ignore(std::string const& x)
    {
        // Split the input into a list of words to ignore.
        std::stringstream  words(x);
        std::copy(std::istream_iterator<std::string>(words),
                std::istream_iterator<std::string>(words),
                std::back_inserter(ignoreItemVector)
                );
    }
};
template<>
std::istream& operator>><std::string>(std::istream& stream, ignore<std::string> const& test)
{
    // Specifically ignore each word.
    foreach(std::string const& loop, test.ignoreItemVector)
    {
        std::string   next;
        if ((stream >> next) && (loop != next))       // if the stream already failed
        {    stream.setstate(std::ios::badbit);      // then don't change anything
        }                                            // as it may confuse people
    }
    return stream;
}