Std::fstream читает блок данных из файла и записывает данные обратно в файл, пока не завершится EOF

Я читаю блоки данных из файла, но не все сразу (например, 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;
}

Вы видите, что может быть причиной плохой перезаписи?

Обновлено: Извините, я не вижу, как связанный дубликат отвечает на мой вопрос, я просто не могу применить данный ответ к приведенному выше коду.

Это работает для меня. У вас есть проблема в том, что вы не очень хорошо справляетесь с условием конца файла.

Omnifarious 09.04.2019 20:19

Как это работает для вас? Можете дать код, который работает?

user11157650 09.04.2019 20:26

Я запустил точный код, который у вас есть.

Omnifarious 09.04.2019 20:32

хм, тогда в чем проблема, почему файл не переписывается должным образом на моей машине? Я не вижу ничего определенного в реализации? код должен работать одинаково на всех машинах, верно?

user11157650 09.04.2019 20:36

Возможный дубликат Чтение и запись в один и тот же файл с помощью fstream

Alan Birtles 09.04.2019 20:39

Я думаю, что в моей системе происходит то, что как только поток достигает состояния EOF, дальнейшие вызовы ничего не делают. Вы вообще не обрабатываете условие EOF (чтение после конца потока). В вашем случае файл имеет длину 10 символов, и вы читаете его по 3 за раз. Таким образом, последнее чтение пытается прочитать два после конца. Поскольку вы не отслеживаете это, вы записываете три символа из своего буфера, даже если в нем есть только один допустимый символ.

Omnifarious 09.04.2019 20:51

@Omnifarious Вот почему я обнуляю буфер для последнего блока: memset(data,0,BLOCK_SIZE); Я думаю, что это нормально, не уверен.

user11157650 09.04.2019 21:08

@zebanovich - Все в порядке.

Omnifarious 10.04.2019 06:32

Вам просто нужно сделать seekg перед чтением, как указано в дубликате, у меня работает в визуальной студии

Alan Birtles 10.04.2019 07:29

@AlanBirtles большое спасибо, что вы делаете поиск перед чтением, а поиск перед записью сделал свое дело! , ваш комментарий заслуживает того, чтобы быть принятым в качестве ответа.

user11157650 11.04.2019 10:46

Нет, это просто дубликат ответа в другом вопросе, повторение ничего не даст.

Alan Birtles 11.04.2019 13:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
11
599
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш код плохо обрабатывает условие 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());

Я поместил это в свой код в соответствующее место.

Я скопировал ваш код, и это приводит к бесконечному циклу.

user11157650 09.04.2019 20:53

@zebanovich - в моей системе работает нормально. Похоже, у вас может быть глючная реализация iostream. Какой компилятор и какая платформа?

Omnifarious 09.04.2019 20:57

Это действительно озадачивает, Windows 10 x64, msvc-v142 (предварительная версия VS 2019)

user11157650 09.04.2019 21:00

почему кастинг - reinterpret_cast<char > требуется? VS2019 предлагает использовать только (char)

Build Succeeded 20.10.2020 08:14

Другие вопросы по теме