我正在实现一个生成结果的东西,并将它们写入某种格式的文件。
相当简单,但我希望这是动态的。
我会抛弃几节课。
数据 - 所有结果的基类
DataFile - 所有文件格式类型的基类,方法为addData(Data * data)
ErrorData - 从数据派生,包含有关错误的数据。
InfoData - 从数据派生,包含一般信息。
XmlFile - 派生自DataFile,包含XML格式的数据。
BinaryFile - 从DataFile派生,包含二进制格式的数据。
我的问题是:
我将实施如何将 ErrorData 写入 XmlFile ?
我不喜欢在答案中看到:
我知道我的基本C ++虚拟化和事情,不需要过于具体。
我很欣赏一些代码片段,但它们不那么模糊。
我对制作 DataWriter 基类有一些想法,并从那些知道如何编写 数据 <的类中派生类/ strong>特定类型的某种类型的 DataFile ,但我对此具体细节有点不确定。
编辑:
我将以一个例子的形式澄清一点。
我们有两种新的文件格式, FormatATxtFile 和 FormatBTxtFile 。
假设我们有一个 InfoData 对象,它有参数:
讯息的行号:34
消息内容:Hello World
写入 FormatATxtFile 的对象在文件中如下所示:
行:34; Txt:Hello World;类型:信息
在 FormatBTxtFile 中,它看起来像这样:
@ Info,34,Hello World
一种将数据导出为其他格式的方法。我至少现在不需要导入。
使用此代码的代码如下:
DataFile * file = DataFileFactory::createFile(type);
std::vector<Data*> data = generateData();
file->setData(data);
file->writeTo("./FileName"); // correct end is added by DataFile type, ie .txt
编辑:
似乎我没有弄清楚Xml和二进制文件格式出现什么问题。对不起。
让我们使用与上面相同的 InfoData 对象并将其推送到XmlFile格式。它可能会在某个元素下产生类似的东西:
<InfoLog>
<Info Line="34">Hello World</Info>
</InfoLog>
我们假设 ErrorData 类具有以下参数:
错误的行号:56
错误文本:LINK:致命错误LNK1168
错误消息之前的10行:text1 ...
错误信息后的10行:text2 ...
整个记录发生的事情:text3 ...
现在,当它被推入XML格式时,它将需要完全不同的东西。
<Problems>
<Error>
<TextBefore>text1...</TextBefore>
<Text line = 56>
LINK : fatal error LNK1168
</Text>
<TextAfter>text1...</TextAfter>
</Error>
...
</Problems>
整个文件可能如下所示:
<Operation>
<InfoLog>
<Info Line="34">Hello World</Info>
<Info Line="96">Goodbye cruel World</Info>
</InfoLog>
<Problems>
<Error>
<TextBefore>text1...</TextBefore>
<Text line = 56>
LINK : fatal error LNK1168
</Text>
<TextAfter>text1...</TextAfter>
</Error>
<Error>
<TextBefore>sometext</TextBefore>
<Text line = 59>
Out of cheese error
</Text>
<TextAfter>moretext</TextAfter>
</Error>
</Problems>
</Operation>
答案 0 :(得分:2)
不是试图找到一个把它放在一个类中的地方,而是一个新函数呢?
void copyData(const ErrorData *data, DataFile *output)
{
// ...
}
然后,您可以为要转换的任何数据类型重载此函数。
或者,您也许可以使用模板:
template<typename A, typename B> copyData(const A *data, const B *output);
然后,您可以为需要支持的特定类型专门化模板。
答案 1 :(得分:2)
像标准库一样 - 与virtual
函数/运算符一起使用。我们都可以使用istream&
并使用operator>>
从中提取我们想要的内容,而我们完全不关心基础流,无论是cin
,fstream
还是stringstream
。而是通过引用(data
)来获取Data& data
。
答案 2 :(得分:1)
如果您考虑下面的代码,它提供了如何任意组合通用字段访问与通用字段流的最小说明 - 分解您的各种要求。如果适用性或实用性不明确,请告诉我....
#include <iostream>
#include <string>
struct X
{
int i;
double d;
template <typename Visitor>
void visit(Visitor& visitor)
{
visitor(i, "i");
visitor(d, "d");
}
};
struct XML
{
XML(std::ostream& os) : os_(os) { }
template <typename T>
void operator()(const T& x, const char name[]) const
{
os_ << '<' << name << '>' << x << "</" << name << ">\n";
}
std::ostream& os_;
};
struct Delimiter
{
Delimiter(std::ostream& os,
const std::string& kvs = "=", const std::string& fs = "|")
: os_(os), kvs_(kvs), fs_(fs)
{ }
template <typename T>
void operator()(const T& x, const char name[]) const
{
os_ << name << kvs_ << x << fs_;
}
std::ostream& os_;
std::string kvs_, fs_;
};
int main()
{
X x;
x.i = 42;
x.d = 3.14;
XML xml(std::cout);
Delimiter delimiter(std::cout);
x.visit(xml);
x.visit(delimiter);
}
答案 3 :(得分:1)
我一直在玩你的问题,这就是我的想法:
#include <iostream>
#include <list>
#include <map>
#include <string>
using namespace std;
class DataFile;
class Data {
public:
virtual void serializeTo(DataFile*) = 0;
};
class DataFile {
public:
void addData(Data* d) {
_data.push_back(d);
}
virtual void accept(string paramName, string paramValue) {
_map[paramName] = paramValue;
}
virtual void writeTo(string const& filename) = 0;
protected:
list<Data*> _data;
map<string, string> _map;
};
class FormatATxtFile: public DataFile {
public:
void writeTo(string const& filename) {
cout << "writing to " << filename << ".txt:" << endl;
for(list<Data*>::iterator it = _data.begin(); it != _data.end(); ++it) {
(*it)->serializeTo(this);
cout << "Line:" << _map["Line"] << "; "
<< "Txt:" << _map["Txt"] << "; "
<< "Type: " << _map["Type"]
<< endl;
}
cout << endl;
}
};
class FormatBTxtFile: public DataFile {
public:
void writeTo(string const& filename) {
cout << "writing to " << filename << ".b-txt" << endl;
for(list<Data*>::iterator it = _data.begin(); it != _data.end(); ++it) {
(*it)->serializeTo(this);
cout << "@" << _map["Type"] << "," << _map["Line"] << "," << _map["Txt"]
<< endl;
}
cout << endl;
}
};
class InfoData: public Data {
public:
void serializeTo(DataFile* storage) {
storage->accept("Line", line);
storage->accept("Txt", txt);
storage->accept("Type", "Info");
}
string line;
string txt;
};
int main()
{
FormatATxtFile fileA;
FormatBTxtFile fileB;
InfoData info34;
info34.line = "34";
info34.txt = "Hello World";
InfoData info39;
info39.line = "39";
info39.txt = "Goodbye cruel World";
fileA.addData(&info34);
fileA.addData(&info39);
fileB.addData(&info34);
fileB.addData(&info39);
fileA.writeTo("./Filename");
fileB.writeTo("./Filename");
}
实际上,它并没有真正写入文件,但它很容易根据您的需要进行更改。
此示例代码的输出为:
写给./Filename.txt:行:34;
Txt:Hello World;类型:信息热线:39;
Txt:再见残酷的世界;类型:信息写给./Filename.b-txt
@ Info,34,Hello World
@ Info,39,Goodbye cruel World
如您所见,Data
需要为DataFile
提供由名称和值标识的参数,DataFile
的每个专精都会按照自己喜欢的方式处理它。
HTH