Я читаю блоки данных из файла, но не все сразу (например, 3 байта на чтение/запись), а затем записываю те же 3 байта обратно в файл в ту же позицию внутри файла, а затем продолжаю цикл, пока не будет больше нет блоков для чтения.
Другими словами, я пытаюсь переписать файл по его содержимому.
Однако есть проблема, что окончательный результат не такой, как в начале.
Следующий пример кода считывает 3 байта за итерацию из файла «sample.txt», содержимое файла простое:
0123456789
после чтения данных и записи данных обратно в файл содержимое:
012345345345
Как видите, по какой-то причине данные не переписываются правильно.
#include <fstream>
#include <iostream>
using namespace std;
#define BLOCK_SIZE 3
int main()
{
// open file
fstream file;
file.open("sample.txt", ios::binary | ios::out | ios::in);
// determine size and number of blocks to read
file.seekg(0, ios::end);
streampos size = file.tellg();
int blocks = size / BLOCK_SIZE;
cout << "size:\t" << size << endl;
if (size % BLOCK_SIZE != 0)
{
++blocks;
}
cout << "blocks:\t" << blocks << endl;
// return to beginning
file.seekg(ios::beg);
// we will read data here
unsigned char* data = new unsigned char[BLOCK_SIZE];
streampos pos;
// read blocks of data and write data back
for (int i = 0; i < blocks; ++i)
{
pos = file.tellg();
cout << "before read:\t" << pos << endl;
// read block
file.read(reinterpret_cast<char*>(data), BLOCK_SIZE);
cout << "after read:\t" << file.tellg() << endl;
// write same block back to same position
file.seekp(pos);
cout << "before write:\t" << file.tellg() << endl;
file.write(reinterpret_cast<char*>(data), BLOCK_SIZE);
cout << "after write:\t" << file.tellg() << endl;
// reset buffer
memset(data, 0, BLOCK_SIZE);
}
file.close();
delete[] data;
cin.get();
return 0;
}
Вы видите, что может быть причиной плохой перезаписи?
Обновлено: Извините, я не вижу, как связанный дубликат отвечает на мой вопрос, я просто не могу применить данный ответ к приведенному выше коду.
Как это работает для вас? Можете дать код, который работает?
Я запустил точный код, который у вас есть.
хм, тогда в чем проблема, почему файл не переписывается должным образом на моей машине? Я не вижу ничего определенного в реализации? код должен работать одинаково на всех машинах, верно?
Возможный дубликат Чтение и запись в один и тот же файл с помощью fstream
Я думаю, что в моей системе происходит то, что как только поток достигает состояния EOF, дальнейшие вызовы ничего не делают. Вы вообще не обрабатываете условие EOF (чтение после конца потока). В вашем случае файл имеет длину 10 символов, и вы читаете его по 3 за раз. Таким образом, последнее чтение пытается прочитать два после конца. Поскольку вы не отслеживаете это, вы записываете три символа из своего буфера, даже если в нем есть только один допустимый символ.
@Omnifarious Вот почему я обнуляю буфер для последнего блока: memset(data,0,BLOCK_SIZE); Я думаю, что это нормально, не уверен.
@zebanovich - Все в порядке.
Вам просто нужно сделать seekg
перед чтением, как указано в дубликате, у меня работает в визуальной студии
@AlanBirtles большое спасибо, что вы делаете поиск перед чтением, а поиск перед записью сделал свое дело! , ваш комментарий заслуживает того, чтобы быть принятым в качестве ответа.
Нет, это просто дубликат ответа в другом вопросе, повторение ничего не даст.
Ваш код плохо обрабатывает условие EOF и оставляет поток в плохом состоянии после попытки прочитать за конец файла. В моей системе это приводит к тому, что все дальнейшие вызовы потока не имеют никакого эффекта. Могу поспорить, что в вашей системе это не так (я подозреваю, что это ошибка в ее iostream
реализации). Я переделал ваш код, чтобы правильно обработать условие EOF, а также сделать его намного чище несколькими другими способами:
#include <fstream>
#include <iostream>
using namespace std;
const int BLOCK_SIZE = 3;
int main()
{
// open file
fstream file;
file.open("sample.txt", ios::binary | ios::out | ios::in);
// we will read data here
bool found_eof = false;
// read blocks of data and write data back
while (!found_eof)
{
unsigned char data[BLOCK_SIZE] = {0};
char * const data_as_char = reinterpret_cast<char *>(data);
streampos const pos = file.tellp();
int count_to_write = BLOCK_SIZE;
cout << "before read:\t" << file.tellg() << ' ' << pos << '\n';
// read block
if (!file.read(data_as_char, BLOCK_SIZE)) {
found_eof = true;
count_to_write = file.gcount();
file.clear();
cout << "Only " << count_to_write << " characters extracted.\n";
}
cout << "after read:\t" << file.tellg() << ' ' << file.tellp() << '\n';
// write same block back to same position
file.seekp(pos);
cout << "before write:\t" << file.tellg() << ' ' << file.tellp() << '\n';
file.write(data_as_char, count_to_write);
cout << "after write:\t" << file.tellg() << ' ' << file.tellp() << '\n';
file.seekp(file.tellp());
}
file.close();
cin.get();
return 0;
}
Но принципиально это не отличается. Обе версии работают у меня одинаково. Я на Linux с g++.
Из ссылки на возможный обман я бы также предложил добавить это непосредственно перед закрытием }
вашего цикла for
:
file.seekp(file.tellp());
Я поместил это в свой код в соответствующее место.
Я скопировал ваш код, и это приводит к бесконечному циклу.
@zebanovich - в моей системе работает нормально. Похоже, у вас может быть глючная реализация iostream
. Какой компилятор и какая платформа?
Это действительно озадачивает, Windows 10 x64, msvc-v142 (предварительная версия VS 2019)
почему кастинг - reinterpret_cast<char > требуется? VS2019 предлагает использовать только (char)
Это работает для меня. У вас есть проблема в том, что вы не очень хорошо справляетесь с условием конца файла.