如何将A类数据写入B类格式

How to write data of type A to format of type B

本文关键字:格式 数据      更新时间:2023-10-16

我正在实现一个生成结果并将它们写入特定格式的文件的东西。

相当简单,但我希望它是动态的。

我会安排一些课程。

Data -所有结果的基类
DataFile -所有文件格式类型的基类,具有方法addData(Data * data)

ErrorData —源自Data,包含有关错误的数据。
InfoData—派生自Data,包含泛型信息。

XmlFile -派生自DataFile,包含XML格式的数据
BinaryFile -从DataFile派生,包含二进制格式的数据

我的问题是:

我在哪里把实现如何写ErrorData到一个XmlFile?

我不喜欢看到的答案:

  1. Data DataFileErrorDataXmlFile(因为这意味着我需要在每次添加新的DataDataFile派生类时添加这些)
  2. 类型转换到派生类型。
  3. 一般的丑陋:)

我知道基本的c++虚拟化和东西,不需要特别具体。

我确实欣赏一些代码片段,它们留下了较少的歧义。

我有一些想法,使DataWriter基类和派生类,将知道如何编写数据特定类型的到特定类型的DataFile,但我有点不确定的细节。

编辑:

我将以一个示例的形式进一步说明。

让我们有两种新的文件格式,FormatATxtFileFormatBTxtFile

让我们假设我们有一个InfoData对象,它有参数:

留言行号:34
消息内容:Hello World

对象写入FormatATxtFile,在文件中如下所示:

: 34; Txt: Hello World;类型:信息

FormatBTxtFile它看起来像这样:

34岁的

@Info 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: fatal Error 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>

而不是试图找到一个地方把这个类,关于一个新的功能?

void copyData(const ErrorData *data, DataFile *output)
{
    // ...
}

你可以为任何你想转换的数据类型重载这个函数。

或者,您也可以使用模板:

template<typename A, typename B> copyData(const A *data, const B *output);

然后你可以为你需要支持的特定类型特殊化模板。

像标准库一样使用virtual函数/操作符。我们都可以使用istream&并从operator>>中提取我们想要的东西,而我们完全不关心底层流,无论是cin, fstream还是stringstream。而把你的data参考然后(Data& data)。

如果您考虑下面的代码,它提供了如何任意组合通用字段访问和通用字段流的最小示例-分解您的各种需求。如果适用性或实用程序不清楚,请告诉我....

#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);
}

我一直在思考你的问题,这就是我的想法:

#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");    
}

实际上,它并没有真正写入文件,但是很容易修改以满足您的需要。

示例代码的输出是:

write to ./Filename.txt: Line:34;Txt: Hello World;类型:信息行:39;
再见,残酷的世界;类型:Info

写入。/Filename.b-txt
34岁的@Info Hello World
@Info,39,再见残酷的世界

如您所见,Data需要向DataFile提供由名称和值标识的参数,DataFile的每个专门化将以自己喜欢的方式处理它。

HTH