Stream Manipulation用于输出不同格式的对象数据

时间:2016-02-10 23:54:22

标签: c++ iostream ostream

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

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;

2 个答案:

答案 0 :(得分:2)

首先,除非真的需要将其作为单独的操纵符实现,否则请考虑其他路径。两个显而易见的可能性是自定义区域设置,或者只是执行格式化并将结果作为字符串返回的函数。前者看起来像:

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<<会读取它们以控制其行为。它很笨拙和丑陋,但如果你愿意付出一点努力,它确实有效。

答案 1 :(得分:1)

  

我如何使用流操纵器进行此操作?

我会告诉你如何,但请记住,这不是去这里的方式。专用(成员)功能或一些花哨的OOP模式是一种更好的方法。

那就是说你可以将任意数据附加到流对象上。为此,您首先需要该数据的“id”。你可以使用(update m k (fnil conj []) v)

std::ios_base::xalloc

使用返回的号码,您可以通过static int formatId = ios_base::xalloc(); 返回的引用获得(写入)long的访问权限。 (还有std::ios_base::iword来获得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;
}

(Live here)