4

将数据存储到网络上的文件的最佳方式是什么,稍后将以编程方式再次读取该文件。该程序的目标平台是 Linux (Fedora),但需要将文件写入 Windows (XP) 机器

这需要在 C++ 中,会有大量的写入/读取事件,因此它需要高效,并且需要以可以轻松读回的方式写出数据。

整个文件可能没有被读回,我需要在文件中搜索特定的数据块并将其读回。

简单的二进制流编写器可以吗?我应该如何存储数据 - XML?

还有什么我需要担心的吗?


更新:为了澄清,这里有一些彼得陈观点的答案

请说清楚:

* 你只附加块,还是你还需要删除/更新它们?

我只需要追加到文件的末尾,但需要搜索并从其中的任何点检索

*** are all blocks of the same size?**

不,数据的大小会有所不同 - 有些是自由文本评论(如此处的帖子),有些是特定的对象状数据(参数集)

*** is it necessary to be a single file?**

不,但可取

*** by which criteria do you need to locate blocks?**

按数据类型和时间戳。例如,如果我定期写出一组特定的参数,在其他数据中,比如自由文本,我想在某个日期/时间找到这些参数的值 - 所以我需要搜索我的时间写出最接近该日期的那些参数并将它们读回。

*** must the data be readable for other applications?**

不。

*** do you need concurrent access?**

是的,我可能会在阅读时继续写作。但一次只能写一篇。

*** Amount of data (per block / total) - kilo, mega, giga, tera?**

每次写入的数据量会很低......从几个字节到几百个字节 - 总共应该看到不超过几百千字节,可能是 fwe 兆字节。(目前还不确定)

**> 如果你需要所有这些,自己动手将是一个挑战,我肯定会的

建议使用数据库。如果您需要的更少,请指定以便我们推荐。**

数据库会使系统过于复杂,因此不幸的是,这不是一种选择。

4

9 回答 9

5

你的问题太笼统了。我会首先定义我的需求,然后定义文件的记录结构,然后使用文本表示来保存它。看看Eric Stone Raymond 的数据元格式,在JSON中,也许是CSVXML。peterchen 的所有观点似乎都是相关的。

于 2008-10-27T13:39:42.247 回答
3

会有大量的写入/读取事件,因此它需要高效,

那将没有效率。

在 Win2K 时代,我在这方面做了很多时间,当时我必须实现一个本质上包含文件副本的程序。我发现到目前为止,我的程序中最大的瓶颈似乎是每个 I/O 操作的开销。我发现在减少总运行时间方面最有效的一项是减少我请求的 I/O 操作的数量。

我开始做漂亮的流 I/O,但这并不好,因为愚蠢的编译器正在为每个字符发出一个 I/O。与 shell 的“复制”命令相比,它的性能实在是太差了。然后我试着一次写出一整行,但这只是稍微好一点。

最终,我编写了程序,试图将整个文件读入内存,这样在大多数情况下,只有 2 个 I/O:一个用于读入,另一个用于写出。这就是我看到巨大节省的地方。处理手动缓冲所涉及的额外代码在等待 I/O 完成的更短的时间内得到了弥补。

当然,这是大约 7 年前的事了,所以我想现在情况可能大不相同。如果您想确定,请自行计时。

于 2008-10-27T14:28:58.947 回答
3

可能您应该有另一个文件,该文件将被读入具有固定大小数据的向量。

struct structBlockInfo
    {
        int iTimeStamp;    // TimeStamp 
        char cBlockType;   // Type of Data (PArameters or Simple Text)
        long vOffset;      // Position on the real File
    };

每次您添加一个新块时,您也会将其添加到该向量中对应的信息并保存。

现在,如果你想读取一个特定的块,你可以在这个向量上进行搜索,将自己定位在“真实文件”上,使用 fseek(或其他)到相应的偏移量,然后读取 X 个字节(这个偏移量到其他或文件末尾)并根据 cBlockType 对某些内容进行强制转换,例如:

    struct structBlockText
    {   
        char cComment[];
    };

    struct structBlockValuesExample1
    {   
        int iValue1;
        int iValue2;
    };

    struct structBlockValuesExample2
    {   
        int iValue1;
        int iValue2;
        long lValue1;
        char cLittleText[];
    };

读一些字节......

fread(cBuffer, 1, iTotalBytes, p_File);

如果它是一个块文本...

structBlockText* p_stBlock = (structBlockText*) cBuffer;

如果它是一个 structBlockValuesExample1...

structBlockValuesExample1* p_stBlock = (structBlockValuesExample1*) cBuffer;

注意:cBuffer 可以容纳多个 Block。

于 2008-10-27T15:57:03.373 回答
1

您的应用程序听起来需要一个数据库。如果您负担得起,请使用一个。但是不要使用像 sqlite 这样的嵌入式数据库引擎来通过网络存储文件,因为它可能对您的目的来说太不稳定了。如果你仍然想使用类似的东西,你必须通过你自己的读写器进程和你自己的访问协议来使用它,如果你使用像 XML 这样的基于文本的文件格式,稳定性问题仍然存在,所以你必须这样做他们也一样。

不过,如果不知道您的工作量,我无法确定。

于 2008-10-27T13:49:41.693 回答
1

这就是我用于读取/写入数据的内容:

template<class T>
    int write_pod( std::ofstream& out, T& t )
{
    out.write( reinterpret_cast<const char*>( &t ), sizeof( T ) );
    return sizeof( T );
}

template<class T>
    void read_pod( std::ifstream& in, T& t )
{
    in.read( reinterpret_cast<char*>( &t ), sizeof( T ) );
}

这不适用于向量、双端队列等,但只需写出后跟数据的项目数即可轻松完成:

struct object {
    std::vector<small_objects> values;

    template <class archive>
    void deserialize( archive& ar ) {
        size_t size;
        read_pod( ar, size );
        values.resize( size );
        for ( int i=0; i<size; ++i ) {
            values[i].deserialize( ar );
        }
    }
}

当然,您需要实现序列化和反序列化功能,但它们很容易实现......

于 2008-10-27T16:29:28.003 回答
1

我会查看Boost 序列化库

他们的例子之一是:

#include <fstream>

// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

/////////////////////////////////////////////////////////////
// gps coordinate
//
// illustrates serialization for a simple type
//
class gps_position
{
private:
    friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    int minutes;
    float seconds;
public:
    gps_position(){};
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}
};

int main() {
    // create and open a character archive for output
    std::ofstream ofs("filename");

    // create class instance
    const gps_position g(35, 59, 24.567f);

    // save data to archive
    {
        boost::archive::text_oarchive oa(ofs);
        // write class instance to archive
        oa << g;
        // archive and stream closed when destructors are called
    }

    // ... some time later restore the class instance to its orginal state
    gps_position newg;
    {
        // create and open an archive for input
        std::ifstream ifs("filename");
        boost::archive::text_iarchive ia(ifs);
        // read class state from archive
        ia >> newg;
        // archive and stream closed when destructors are called
    }
    return 0;
}
于 2008-10-27T19:53:16.140 回答
1

如果您只谈论几兆字节,我根本不会存储在磁盘上。在网络上有一个进程接受数据并将其存储在内部,并且还接受对该数据的查询。如果需要记录数据,这个过程也可以将其写入磁盘。请注意,这听起来很像数据库,这确实可能是最好的方法。我不明白这如何使系统复杂化。事实上,它使事情变得容易得多。只需编写一个抽象数据库的类,并让其余代码使用它。

过去我自己也经历过同样的过程,包括将数据库视为过于复杂而忽略。它开始时相当简单,但几年后,我们编写了自己的、实现不佳、有缺陷、难以使用的数据库。那时,我们丢弃了我们的代码并转移到了 postgres。我们从不后悔改变。

于 2008-10-27T15:42:20.203 回答
1

您需要查看您正在写出的数据类型。一旦你处理的是对象而不是 POD,简单地写出对象的二进制表示不一定会产生任何你可以成功反序列化的东西。

如果您“仅”写出文本,那么如果您以相同的文本表示形式写出数据,则读回数据应该相对容易。如果你试图写出更复杂的数据类型,你可能需要查看 boost::serialization 之类的东西。

于 2008-10-27T13:42:41.973 回答
0

如果您不进行文本存储,请将其存储为二进制文件。文本效率低得可怕;XML 更糟糕。存储格式缺乏效率预示着更大的文件传输,这意味着更多的时间。如果您必须存储文本,请通过 zip 库对其进行过滤。

您的主要问题将是文件锁定和并发性。当您必须以并发方式进行写/读/写时,一切都开始变得轻松起来。在这一点上,安装某种数据库并将文件 BLOB 或其他东西,因为此时您将编写自己的数据库......并且没有人想重新发明那个轮子(你知道,如果他们不是'不做自己的数据库公司,或者是博士生,或者有一个奇怪的爱好......)

于 2008-10-27T14:50:12.227 回答