c ++ fstream - 创建自己的格式化标志

时间:2012-03-09 06:38:23

标签: c++ fstream flags

我需要为输出文件的格式创建新标志。我有一个班级

class foo{
    bar* members;
    ofstream& operator<<(ofstream&);
    ifstream& operator>>(ifstream&);
};

我希望像以下一样使用它:

fstream os('filename.xml');
foo f;
os << xml << f;
os.close();

这将保存 xml 文件。

fstream os('filename.json');
foo f;
os << json << f;
os.close();

这是 json 文件。

我该怎么做?

3 个答案:

答案 0 :(得分:6)

您可以轻松创建自己的操纵者,劫持现有的操纵者 标记或使用std::ios_base::xalloc获取特定的新流 记忆,例如(在Foo的实施文件中:

static int const manipFlagId = std::ios_base::xalloc();

enum
{
    fmt_xml,        //  Becomes the default.
    fmt_json
};

std::ostream&
xml( std::ostream& stream )
{
    stream.iword( manipFlagId ) = fmt_xml;
    return stream;
}

std::ostream&
json( std::ostream& stream )
{
    stream.iword( manipFlagId ) = fmt_json;
    return stream;
}

std::ostream&
operator<<( std::ostream& dest, Foo const& obj )
{
    switch ( dest.iword( manipFlagId ) ) {
    case fmt_xml:
        // ...
        break;
    case fmt_json:
        //  ...
        break;
    default:
        assert(0);  //  Or log error, or abort, or...
    }
    return dest;
}

在标题中声明xmljson,然后完成工作。

(话虽如此,我宁愿认为这有点滥用 操纵。像xml这样的格式超越了简单的本地格式,以及 最好由一个拥有ostream的单独类来处理 写入整个流,而不仅仅是单个对象。)

答案 1 :(得分:1)

这个问题是iostream库中最大的缺陷。

James Kanze的解决方案是一个可以在你自己的类中工作的部分解决方案,但是一般来说,对象被赋予了一种独特的流式传输方式。

我通常的方法是使用可以传递到流中的函数创建我自己的包装类,并且xml将包含xml_node()xml_attribute()的重载,例如

os << xml_attribute( "Id", id );

将属性Id设置为xml格式的变量中的任何内容。

我也编写了节点作用域,因此他们会编写流程来构建构造中的节点打开文本,并在销毁时自动编写结束逻辑。

我的方法优于James Kanze的解决方案的优势在于它是可扩展的。我认为James Kanze的结束评论表明他不支持他的解决方案,并且可能会使用更像我的方法。

使用上述解决方案,为了添加更多格式,您必须编辑运算符&lt;&lt;遍布各处的函数,而json格式代码将是一组完全不同的函数,如果你添加了另一种格式,你可以为它添加代码,而无需编辑任何现有代码。

顺便说一下,对于XML,您可以使用现有的DOM或SAX解析器,而不会直接以这种方式使用iostream。

答案 2 :(得分:0)

最简单的方法是首先创建一个标记类型及其单个实例:

struct JsonStreamTag {} json;

然后让这样的标签构造一个对象来包装流:

class JsonStream {
public:

    // (1)
    friend JsonStream operator<<(std::ostream& ostream, const JsonStreamTag&) {
        return JsonStream(ostream);
    }

    // (2)
    template<class T>
    friend JsonStream& operator<<(JsonStream& json_stream, const T& value) {
        write_json(json_stream.ostream, value); // (3)
        return json_stream;
    }

protected:

    JsonStream(std::ostream& ostream) : ostream(ostream) {}

private:

    std::ostream& ostream;

};

构造函数为protected,以确保您只能使用some_ostream << json(1)构建JsonStream。另一个插入操作符(2)执行实际格式化。然后,为每个相关类型定义write_json()(3)的重载:

void write_json(std::ostream& stream, int value) {
    stream << value;
}

void write_json(std::ostream& stream, std::string value) {
    stream << '"' << escape_json(value) << '"';
}

// Overloads for double, std::vector, std::map, &c.

或者,省略(2)并为operator<<(JsonStream&, T)添加重载。

然后,按照相同的流程,使用XmlStreamXmlStreamTag撰写相应的write_xml()。这假设您的输出可以完全根据您正在编写的特定值构建;如果你需要在你要编写的每个文件中使用相同的页眉或页脚,只需使用构造函数和析构函数:

XmlStream(std::ostream& ostream) : ostream(ostream) {
    ostream << "<?xml version=\"1.0\"?><my_document>"
}

~XmlStream() {
    ostream << "</my_document>";
}