如何更新二进制文件中的结构项
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 循环中写入每个元素。
【讨论】:
&vec[0]
可用于向量序列化。更多信息:Getting array from std:vector【参考方案3】:
您可以通过这种方式将结构序列化为文件:
写入字符串长度(4字节) 自己写字符串。 向量的写入长度(以字节为单位,以后更容易解析)。 写入矢量数据。&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 );
【讨论】:
“字符串长度”和“向量长度”对我有什么帮助?以后如何阅读? 如何将新项目添加到文件中的结构向量?以上是关于如何更新二进制文件中的结构项的主要内容,如果未能解决你的问题,请参考以下文章