Как обновить элемент структуры в двоичных файлах

0

У меня есть двоичный файл, который я пишу для него некоторые элементы структуры. Теперь я хочу найти и обновить определенный элемент из файлов. Обратите внимание, что моя структура имеет вектор и его размер не является постоянным.

моя структура:

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
            {

            }

В другом блоке я хочу найти и обновить элемент с его термином (термин уникален).

Теперь я изменил свой код таким образом, чтобы сериализовать мой вектор.

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

Ну, мой главный вопрос по-прежнему остается: как я могу обновить данные, которые я нашел? Есть ли лучший способ их найти?

  • 0
    Вы должны использовать двоичный формат. Почему бы не использовать читаемый человеком формат сериализации?
Теги:
struct
binaryfiles

3 ответа

0
Лучший ответ

Я оценил следующие решения:

  1. обновить в новом файле, переименовать его в старую в конце
  2. обновление в том же файле с потоком с двумя файловыми позициями, чтение и запись, но я не быстро нашел поддержку для такой вещи
  3. обновление в том же файле двумя потоками, чтение и запись, но риск переопределения слишком большой для меня (даже если он защищен от перекрытий)

Поэтому я выбираю первый, самый простой в любом случае.

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

Вопросов?

  • 0
    Ссылка на просмотр кода
  • 0
    Проблема в том, что вы должны сохранять файл после каждой записи (или перед каждым обновлением).
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 );
  • 0
    Как мне помогают длина строки и длина вектора? как я могу прочитать их позже?
  • 0
    Я добавлю обратный (читать) код.
Показать ещё 2 комментария
0

Эта:

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

Не имеет смысла. Вы пишете биты векторной реализации непосредственно на диск. Некоторые из этих бит, скорее всего, будут указателями. Указатели, записанные в файл на диске, не полезны, поскольку они указывают на адресное пространство, принадлежащее процессу, который написал файл, но не будет работать в другом процессе, просматривая тот же файл.

Вам необходимо "сериализовать" ваши данные в файл, например, в цикле for, пишущем каждый элемент.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню