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

时间:2013-12-23 14:05:32

标签: c++ struct binaryfiles

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

我的结构:

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

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

3 个答案:

答案 0 :(得分:0)

此:

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

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

你需要&#34;序列化&#34;您的数据到文件,例如在for循环中编写每个元素。

答案 1 :(得分:0)

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

  • 写入字符串的长度(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 );

答案 2 :(得分:0)

我评估了以下解决方案:

  1. 在新文件中更新,最后将其重命名为旧文件
  2. 使用包含两个文件位置的流在同一文件中进行更新,阅读&amp;写,但我没有迅速找到支持这样的事情
  3. 使用两个流在同一个文件中更新,阅读&amp;写,但潜在的覆盖风险对我来说太大了(即使外面有重叠保护)
  4. 所以我选择第一个,最直截了当。

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

    阅读&amp;写函数与你的函数基本相同,除了我选择不写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;
    }
    

    问题?