【问题标题】:c++ fstream - creating own formatting flagsc++ fstream - 创建自己的格式化标志
【发布时间】:2012-03-09 06:38:23
【问题描述】:

我需要为输出文件的格式创建新的标志。我有课

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 文件。

我该怎么做?

【问题讨论】:

    标签: c++ fstream flags


    【解决方案1】:

    您可以轻松创建自己的操纵器,或者劫持现有的 标志或使用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 的单独类处理,并且 写入整个流,而不仅仅是单个对象。)

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      我也编写了节点范围,因此他们将写入以在构造时流式传输节点打开文本并在销毁时自动编写关闭逻辑。

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

      使用上述解决方案,为了添加更多格式,您必须在各处编辑 operator

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

      【讨论】:

      • 我不认为直接使用流来输出 XML 是一个合适的解决方案。流是流的抽象,而不是分层数据结构。对于输出 XML,我会使用像 Xerces 这样的库;允许插入层次结构的东西。其余的:如果您需要对内置类型或非常复杂的结构进行不寻常的格式设置,包装器类很有用,但操纵器在很多情况下都能很好地工作。
      • 更准确地说(也许这也是你的意思):输出到全局不同的格式应该使用与流不同的机制来完成:自定义操纵器用于单个自定义类型,而不是什么这会影响所有类型的输出。 (而且您不能向int 添加格式选项)。
      【解决方案3】:

      想到的最简单的方法是从创建标签类型及其单个实例开始:

      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 &lt;&lt; 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&lt;&lt;(JsonStream&amp;, T) 添加重载。

      然后只需按照相同的过程使用XmlStreamTagwrite_xml() 编写相应的XmlStream。这假定您的输出可以完全由您正在编写的特定值构成;如果您需要在您将编写的每个文件中都使用相同的页眉或页脚,只需使用构造函数和析构函数:

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

      【讨论】:

      • 我认为这不是一个好主意。例如,它不适用于out &lt;&lt; json &lt;&lt; "Header: " &lt;&lt; obj; 之类的东西。 (当然,对于像 XML 这样的东西,无论如何这都是没有意义的。但这是首先不使用操纵器的论据。)
      • @JamesKanze:这个问题没有明确定义。为了从字面上回答这个问题,我假设“JSON 流”会将所有内容都视为 JSON 值。但我确实认为这样的事情首先是被误导的。
      • XML(和 JSON,据我所知)不是流;它们是更复杂的结构。不相关的类型不应将 XML 或 JSON 输出到 ostream;它应该将其插入到 XML 或 JSON 数据结构的某个实例中,然后在文件级别处理流输出。
      • @JamesKanze:我同意。流用于平面数据,而 JSON 和 XML 本质上都是嵌套的。您可以使用 RAII 来启用基于范围的嵌套 —{ std::cout &lt;&lt; json::object &lt;&lt; ...; } — 但我认为这与仅使用正确的数据结构相比没有优势。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-12
      • 2015-08-27
      • 1970-01-01
      • 1970-01-01
      • 2014-11-28
      • 1970-01-01
      相关资源
      最近更新 更多