如何更新二进制文件中的结构项

Posted

技术标签:

【中文标题】如何更新二进制文件中的结构项【英文标题】:How to update struct item in binary files 【发布时间】:2013-12-23 14:05:31 【问题描述】:

我有一个二进制文件,我向其中写入了一些结构项。现在我想从文件项中查找和更新特定项。 请注意,我的结构有一个向量,它的大小不是恒定的。

我的结构:

struct mapItem

    string term;
    vector<int> pl;
;

将结构项写入文件的代码

if (it==hashTable.end())//didn't find
            
                vector <int> posting;
                posting.push_back(position);                
                hashTable.insert ( pair<string,vector <int> >(md,posting ) );
                mapItem* mi = new mapItem();
                mi->term = md;         
                mi->pl = posting;
                outfile.write((char*)mi, sizeof(mi));

            
            else//finded
            

            

在 else 块中,我想用它的术语查找和更新项目(术语是唯一的)。

现在我已经像这样更改了我的代码来序列化我的向量。

if (it==hashTable.end())//didn't find
            
                vector <int> posting;
                posting.push_back(position);                
                hashTable.insert ( pair<string,vector <int> >(md,posting ) );
                mapItem* mi = new mapItem();
                mi->term = md;         
                mi->pl = posting;

                if(!outfile.is_open())
                    outfile.open("sample.dat", ios::binary | ios::app);

                size_t size = mi->term.size() + 1;
                outfile.write((char*)&size, sizeof(size) );
                outfile.write((char*)mi->term.c_str(), size);
                size = (int)mi->pl.size() * sizeof(int);
                outfile.write((char*)&size, sizeof(size) );
                outfile.write((char*)&mi->pl[0], size );

                outfile.close();
            
            else//finded
            

                (it->second).push_back(position);

                mapItem* mi = new mapItem();

                size_t size;

                if(!infile.is_open())
                
                    infile.open("sample.dat", ios::binary | ios::in);
                

                do
                    infile.read((char*)&size, sizeof(size) ); // string size
                mi->term.resize(size - 1); // make string the right size
                infile.read((char*)mi->term.c_str(), size); // may need const_cast
                infile.read((char*)&size, sizeof(size) ); // vector size
                mi->pl.resize(size / sizeof(int));
                infile.read((char*)&mi->pl[0], size );
                while(mi->term != md);

                infile.close();
            

好吧,我的主要问题仍然存在:我如何更新我找到的数据? 有没有更好的方法找到它们?

【问题讨论】:

是否必须使用二进制格式。为什么不使用人类可读的序列化格式? 【参考方案1】:

我评估了以下解决方案:

    在新文件中更新,最后重命名为旧文件 在同一个文件中使用具有两个文件位置的流进行更新,可读写,但我没有迅速找到对此类事物的支持 在同一个文件中使用两个流进行更新,读取和写入,但底层覆盖的风险对我来说太大(即使在外部受到保护以防止重叠)

所以我选择了第一个,反正是最直接的。

#include <string>
#include <vector>
#include <fstream>
#include <cstdio>
#include <assert.h>

我在你的结构中添加了以下函数:

size_t SizeWrittenToFile() const 

  return 2*sizeof(size_t)+term.length()+pl.size()*sizeof(int);

读写功能与你的基本相同,除了我选择不写入string:c_str()指针(尽管这个丑陋的解决方案应该适用于每个已知的编译)。

bool ReadNext(std::istream& in, mapItem& item)

size_t size;
in.read(reinterpret_cast<char*>(&size), sizeof(size_t));
if (!in)
    return false;

std::istreambuf_iterator<char> itIn(in); 
std::string& out = item.term;
out.reserve(size);
out.clear();    //  this is necessary if the string is not empty
for (std::insert_iterator<std::string> itOut(out, out.begin()); 
     in && (out.length() < size); itIn++, itOut++)
    *itOut = *itIn;

assert(in);
if (!in)
    return false;

in.read(reinterpret_cast<char*>(&size), sizeof(size_t));
if (!in)
    return false;

std::vector<int>& out2 = item.pl;
out2.resize(size);  //  unfortunately reserve doesn't work here
in.read(reinterpret_cast<char*>(&out2[0]), size * sizeof(int));
assert(in);

return true;

//应该添加一个“标题”来标记完整的数据(以“原子”方式写入)

bool WriteNext(std::ostream& out, const mapItem& item)

size_t size = item.term.length();
out.write(reinterpret_cast<const char*>(&size), sizeof(size_t));
if (!out)
    return false;
out.write(item.term.c_str(), size);
if (!out)
    return false;

size = item.pl.size();
out.write(reinterpret_cast<const char*>(&size), sizeof(size_t));
if (!out)
    return false;
out.write(reinterpret_cast<const char*>(&item.pl[0]), size * sizeof(int));
if (!out)
    return false;

return true;

更新函数如下所示:

bool UpdateItem(std::ifstream& in, std::ofstream& out, const mapItem& item)

mapItem it;
bool result;
for (result = ReadNext(in, it); result && (it.term != item.term); 
     result = ReadNext(in, it))
    if (!WriteNext(out, it))
        return false;
if (!result)
    return false;

//  write the new item content
assert(it.term == item.term);
if (!WriteNext(out, item))
    return false;

for (result = ReadNext(in, it); result; result = ReadNext(in, it))
    if (!WriteNext(out, it))
        return false;

//  failure or just the end of the file?
return in.eof();


bool UpdateItem(const char* filename, const mapItem& item)

std::ifstream in(filename);
assert(in);

std::string filename2(filename);
filename2 += ".tmp";
std::ofstream out(filename2.c_str());
assert(out);

bool result = UpdateItem(in, out, item);
//  close them before delete
in.close();
out.close();

int err = 0;

if (result)

    err = remove(filename);
    assert(!err && "remov_140");
    result = !err;

if (!result)

    err = remove(filename2.c_str());
    assert(!err && "remov_147");

else

    err = rename(filename2.c_str(), filename);
    assert(!err && "renam_151");
    result = !err;


return result;

问题?

【讨论】:

Code review link 麻烦的是每次写入后(或每次更新前)都必须保存文件。【参考方案2】:

这个:

outfile.write((char*)mi, sizeof(mi));

没有意义。您正在将向量实现的位直接写入磁盘。其中一些位极有可能是指针。写入磁盘文件的指针没有用处,因为它们指向属于写入文件的进程的地址空间,但在读取同一文件的另一个进程中不起作用。

您需要将数据“序列化”到文件中,例如在 for 循环中写入每个元素。

【讨论】:

&amp;vec[0] 可用于向量序列化。更多信息:Getting array from std:vector【参考方案3】:

您可以通过这种方式将结构序列化为文件:

写入字符串长度(4字节) 自己写字符串。 向量的写入长度(以字节为单位,以后更容易解析)。 写入矢量数据。 &amp;vec[0] 是第一个元素的地址。您可以在一个镜头中写入所有元素,因为此缓冲区是连续的。

写:

size_t size = mi->term.size() + 1;
outfile.write((char*)&size, sizeof(size) );
outfile.write((char*)mi->term.c_str(), size);
size = (int)mi->pl.size() * sizeof(int);
outfile.write((char*)&size, sizeof(size) );
outfile.write((char*)&mi->pl[0], size );

阅读:

infile.read((char*)&size, sizeof(size) ); // string size
mi->term.resize(size - 1); // make string the right size
infile.read((char*)mi->term.c_str(), size); // may need const_cast
infile.read((char*)&size, sizeof(size) ); // vector size
mi->pl.resize(size / sizeof(int));
infile.read((char*)&mi->pl[0], size );

【讨论】:

“字符串长度”和“向量长度”对我有什么帮助?以后如何阅读? 如何将新项目添加到文件中的结构向量?

以上是关于如何更新二进制文件中的结构项的主要内容,如果未能解决你的问题,请参考以下文章

linux系列 : shell 脚本编程

如何检测二进制/库中的未定义符号?

如何找到二进制文件的哪个部分需要某个版本的某个依赖项?

C语言 结构体指针字符 二进制文件写入和读取

将包含二进制文件的文件夹结构复制到本机库文件夹

来自 GitHub 发布二进制文件的 NPM 安装/依赖项