С++ iostream двоичные проблемы чтения и записи

118
11

Правильно, пожалуйста, медведь со мной, поскольку у меня есть две отдельные попытки, которые я расскажу ниже.

Сначала я начал читать руководство (http://www.cplusplus.com/doc/tutorial/files/). Однако, хотя он содержит то, что кажется хорошим примером использования read(), в нем нет примера использования функции write().

Сначала я попытался сохранить простой массив символов в двоичном формате, используя write(). Моя оригинальная идея (и надежда) заключалась в том, что я мог бы добавить в этот файл новые записи, используя ios :: app. Первоначально это работало, но я тоже получал нежелательные результаты. Сообщение на другом форуме для справки предложило, что мне не хватало нулевого терминатора в конце моего массива char. Я применил это (или, по крайней мере, попытался на основе того, как я был показан), как видно из приведенного ниже примера. К сожалению, это означало, что read() больше не функционирует должным образом, потому что он не будет считывать нулевой терминатор.

Мне также сказали, что char *memoryBlock - это "злоупотребление" стандартом C++ или что-то еще, и небезопасно, и что я должен вместо этого определить массив точного размера, то есть char memoryBlock[5], однако что, если я хочу написать данные char в файл, который может быть любого размера? Как мне продолжить? В приведенном ниже коде содержатся различные пронумерованные строки кода, указывающие различные попытки, которые я сделал, и различные варианты, в том числе некоторые из предложений, упомянутых выше. Я хочу попытаться использовать код хорошей практики, поэтому, если char *memoryBlock небезопасен или какие-либо другие строки кода, я хочу исправить это.

Я также хотел бы пояснить, что я пытаюсь писать символы здесь только для тестирования, поэтому, пожалуйста, не предлагайте мне вместо этого вместо текстового режима вместо текстового режима. Далее я расскажу о второй части этого вопроса в соответствии с приведенным ниже кодом.

Первый код:

#include <cstdlib>
#include <iostream>
#include <fstream>
//#include <string>

int main()
{
//char memoryBlock[5];
char *memoryBlock;
char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.

std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);

if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";

//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(memoryBlock, (sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();

std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);

if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;

std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();

memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(memoryBlockTwo, size);

std::cout<<memoryBlockTwo<<std::endl;

delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}

Следующая попытка моих работ основывается на том, что попытка использовать ios :: app с ios :: binary просто не будет работать, и чтобы скомпенсировать файл, я должен прочитать всю вещь, внести свои изменения, затем написать и замените все содержимое файла, хотя это кажется несколько неэффективным.

Однако я не читаю и не помещаю содержимое в свой код ниже. То, что я на самом деле пытаюсь сделать, это написать объект пользовательского класса в файл, а затем снова прочитать его обратно.

Кажется, что это работает (хотя, если я здесь делаю что-то плохое по коду, укажите это), ОДНАКО, я, похоже, не могу хранить переменные типа std::string и std::vector потому что я получаю нарушения доступа, когда Я достигаю myFileInput.close(). Поскольку эти переменные-члены закомментированы, нарушение прав доступа не происходит. Мое лучшее предположение о том, почему это происходит, заключается в том, что они используют указатели на другие части памяти для хранения своих файлов, и я сам не записываю данные в свой файл, а указатели на него, которые по-прежнему остаются действительными, когда я читаю мои данные.

Возможно ли вообще хранить содержимое этих более сложных типов данных в файле? Или я должен разбить все на более основные переменные, такие как символы, ints и floats?

Второй код:

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

class testClass
{
public:
testClass()
{
testInt = 5;
testChar = 't';
//testString = "Test string.";
//testVector.push_back(3.142f);
//testVector.push_back(0.001f);
}
testClass(int intInput, char charInput, std::string stringInput, float floatInput01, float floatInput02)
{
testInt = intInput;
testChar = charInput;
testArray[0] = 't';
testArray[1] = 'e';
testArray[2] = 's';
testArray[3] = 't';
testArray[4] = '\0';
//testString = stringInput;
//testVector = vectorInput;
//testVector.push_back(floatInput01);
//testVector.push_back(floatInput02);
}
~testClass()
{}

private:
int testInt;
char testChar;
char testArray[5];
//std::string testString;
//std::vector<float> testVector;
};

int main()
{
testClass testObject(3, 'x', "Hello there!", 9.14f, 6.662f);
testClass testReceivedObject;
//char memoryBlock[5];
//char *memoryBlock;
//char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.

std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);

if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
//memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";

//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN AND NOW";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(reinterpret_cast<char*>(&testObject), (sizeof(testClass)));//(sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();

std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);

if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;

std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
//memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(reinterpret_cast<char *>(&testReceivedObject), size);

//std::cout<<memoryBlockTwo<<std::endl;

//delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}

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

спросил(а) 2021-01-19T19:20:28+03:00 6 месяцев назад
1
Решение
90

В первом примере я не уверен, что вы пишете, поскольку memoryBlock закомментирован и никогда не инициализирован ничем. Когда вы его читаете, поскольку вы используете std::cout для отображения данных на консоли, он ДОЛЖЕН быть завершен NULL или вы будете печатать за пределами буфера памяти, выделенного для memoryBlockTwo.

Либо напишите завершающий нуль в файл:

    memoryBlock = "THEN"; // 4 chars + implicit null terminator
myFile.write(memoryBlock, (sizeof(char)*5));

И/или убедитесь, что буфер завершен после его чтения:

    myFileInput.read(memoryBlockTwo, size);
memoryBlockTwo[size - 1] = '\0';

Во втором примере не делайте этого с объектами C++. Вы обходите необходимые вызовы конструктора, и если вы попытаетесь использовать эти векторы, как вы прокомментировали, это, безусловно, не будет работать так, как вы ожидаете. Если класс - это простые старые данные (не виртуальные функции, не указатели на другие данные), вы, вероятно, будете в порядке, но это по-прежнему очень плохая практика. При сохранении объектов C++ рассмотрите возможность перегрузки операторов << и >>.

ответил(а) 2021-01-19T19:20:28+03:00 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема