【问题标题】:Stream Manipulation for outputting object data in different formats用于以不同格式输出对象数据的流操作
【发布时间】:2016-02-10 23:54:21
【问题描述】:

假设我有一个包含以下数据成员的员工对象:

class Employee {

private:
    int _id;
    std::string _name;
    std::string _address;
    std::string _city;
    std::string _state;
    std::string _country;
    std::string _phone;
    double _salary;
...
}

我想以两种不同的方式输出它:

XML

<Employee>
     <id>12345</id>
     <name>Jack Dough</name>
     <address>24437 Princeton</address>
     <city>Dearborn</city>
     <state>Michigan</state>
     <country>USA</country>
     <phone>303-427-0153</phone>
     <salary>140000</salary>
</Employee>

和类似 JSON 的:

id: 12345
name: Jack Dough
address: 24437 Princeton
city: Dearborn
state: Michigan
country: USA
phone: 303-427-0153
salary: 140000

如何使用流操纵器做到这一点? 例如:

Employee* employee = new Employee(12345, "Jack Dough", "24437 Princeton", "Dearborn", "Michigan", "USA", "303-427-0153", 140000.00);
cout << toXML << employee;
cout << toJSON << employee;

【问题讨论】:

  • 或者我最好的方法是只拥有类函数ostream&amp; Employee::toXML()ostream&amp; Employee::toJSON() 我喜欢流操纵器部分,因为它对我来说看起来更好一些,而且挑战似乎很酷。

标签: c++ iostream ostream


【解决方案1】:

首先,除非您真的需要将此作为单独的操纵器来实现,否则请考虑其他路线。两个明显的可能性是自定义语言环境,或者只是一个执行格式化并将结果作为字符串返回的函数。前者看起来像:

std::locale myLoc(std::locale(), XML_formatter);
cout.imbue(myLoc);

cout << employee;

这使得格式样式在一般流中保持不变。如果你真的需要在同一个流中混合不同的样式,函数版本就简单多了:

std::string toXML(Employee const &e) { 
    std::stringstream ret;

    ret << "Employee>\n<id>" << id << "</id>"
        << // ...
    return ret.str();
}

// ...
cout << toXML(employees[i]);

如果您真的别无选择,只能将其实现为单独的操纵器,则需要存储一个标志来指示流中的当前格式。 Streams以xallociwordpword的形式提供内存管理接口。基本上,xalloc 会为你分配一个词。 iword 使您可以访问该单词作为对 long 的引用,pword 使您可以访问它作为对指向 void 的指针的引用。在您的情况下,您显然只需要一个或两个位,因此您可能想要使用 iword 并定义几个位来指定类型的格式。您的 toXMLtoJSON 操纵器将设置适当的位,您的 operator&lt;&lt; 将读取它们以控制其行为。它既笨拙又丑陋,但如果你愿意付出一点努力,它确实有效。

【讨论】:

  • 我已经想到了,这是一个可行的解决方案,用标志重载 operator
  • 我什至不知道语言环境的可能性,谢谢你指出:)
【解决方案2】:

如何使用流操纵器做到这一点?

我会告诉你怎么做,但请记住,这不是去这里的方式。专用(成员)函数或一些花哨的 OOP 模式是更好的方法。

也就是说,您可以将任意数据附加到流对象。为此,您首先需要该数据的“id”。你可以使用std::ios_base::xalloc

static int formatId = ios_base::xalloc();

使用返回的数字,您可以通过std::ios_base::iword 返回的引用获得(写入)long 的访问权限。 (还有std::ios_base::pword 可以得到void *。)

然后,流操纵器只是可以用流(引用)调用的东西,返回另一个流引用:

ostream & toFoo(ostream & stream) {
  stream.iword(formatId) = 1;
  return stream;
}
ostream & toBar(ostream & stream) {
  stream.iword(formatId) = 2;
  return stream;
}

(注意:邪恶的魔法数字,换成更好的设计!)

这里我只是设置了一个“标志”,以便最后在输出函数(运算符)中检查最后使用了哪个操纵器(如果有):

struct FooBar {};

ostream & operator<<(ostream & stream, FooBar const &) {
  switch (stream.iword(formatId)) {
    case 1: stream << "foo"; break;
    case 2: stream << "bar"; break;
    default: stream << "wild foobar";
  }
  return stream;
}

嗯,就是这样。我测试过:

int main() {
  FooBar f;
  cout << f << toFoo << " " << f << endl;
  cout << f << toBar << " " << f << endl;
  cout << f << endl;
  return 0;
}

(Live here)

【讨论】:

  • 这是一个很好的解决方案。太糟糕的流操纵器没有考虑到这个过程。你碰巧回答了一个我没有问的问题,那就是:如何在流链中关联一个标志并将其关联到重载的operator&lt;&lt;,所以谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 2017-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多