Osikwemen Ответов: 3

Пожалуйста, моя программа продолжает сбой в конце, потому что я использовал функцию fstream:: read (), нужна помощь


пожалуйста, программа вылетает в самом конце моей программы после строки system ("pause").

Что я уже пробовал:

#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
using namespace std;
void eatline() { while(cin.get()!= '\n')continue;
}

 struct sal {
	string x;
	int y;
 };
void writedata(string & name, int x)
{
	 fstream file;
	 file.open("info.dat", ios::out | ios::app | ios::binary);
	if (file.is_open())
	{
		sal l = {
			name,
			x
		};
		file.write(reinterpret_cast<char *>(&l), sizeof(l));
		file.close();
	}
}
void red(vector<sal> & p)
{
	vector<sal>::iterator r = p.begin();
	for (r=p.begin();r!=p.end();r++)
		cout << (*r).x << " " << (*r).y << endl;
}
int main()
{
	long idea;
	string name1;
	cout << "Enter the name of the pupil" << endl;
	while (getline(cin, name1) && name1[0]!='\0')
	{
		cout << "Enter the amount of school fees paid\n";
		cin >> idea;
		eatline();
		writedata(name1, idea);
		cout << "Enter the name of the next pupil(press enter to quit)\n" << endl;
	}
	
	cout << "These are the names and school fess of the pupils you entered\n";
	ifstream fin;
	fin.open("info.dat",ios::in|ios::binary);
	sal k;
	vector<sal>j;
	while (fin.read(reinterpret_cast<char *>(&k), sizeof k))
	{
		j.push_back(k);
	}
	fin.close();
	red(j);
	system("pause");
	return 0;
}

3 Ответов

Рейтинг:
27

Jochen Arndt

Вы пишете и читаете динамические объекты (std::string) в / из файла без обработки динамических данных. Это не сработает.

То std::string объект не содержит саму строку, а только указатель на выделенную память. Depening на целевой сборке (32 или 64 бит), sizeof(std::string) будет 4 или 8 (и sizeof(sal) будет 8 или 16).

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

Если вы хотите сохранить такие данные в файле, вы должны сначала записать размер (длину строки), а затем сохранить содержимое строки. После чтения прочтите длину, предоставьте буфер с этой длиной, прочтите строку в этот буфер и скопируйте ее в файл. std::string.

Непроверенный пример:

void writeSal(fstream & file, const sal & s)
{
    // Get the length
    size_t len = s.name.length();
    // Write length
    file.write(reinterpret_cast<const char *>(&len), sizeof(len));
    // Write string content
    file.write(s.name.c_str(), len);
    // Write y
    file.write(reinterpret_cast<const char *>(&s.y), sizeof(s.y));
}

void readSal(fstream & file, sal & s)
{
    size_t len;
    file.read(reinterpret_cast<char *>(&len), sizeof(len));
    char *buf = new char[len + 1];
    file.read(buf, len);
    buf[len] = '\0';
    s.name = buf;
    delete [] buf;
    file.read(reinterpret_cast<char *>(&s.y), sizeof(s.y));
}


Рейтинг:
2

OriginalGriff

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

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

Мы не можем помочь вам здесь: у нас нет доступа к info.dat файл, который Вы читаете и который содержит данные, вызывающие у вас проблему.

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

Извините, но мы не можем сделать это за вас-вам пора освоить новый (и очень, очень полезный) навык: отладку!


Osikwemen

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

[no name]

Функция объявляется " void writedata(string &name, int x)". Void означает, что вы вообще не возвращаете значение. Просто удалите строку return 0; и повторите попытку. Я только удивляюсь, почему компилятор вообще принимает это.

Osikwemen

Объявление void не относится к основной функции, поэтому оно не влияет на нее

[no name]

Это было бы слишком легко таким образом. Должно быть, я посмотрел не на ту функцию.

Рейтинг:
0

Stefan_Lang

Как сказал Йохен Арндт, вы неправильно сохраняете строку, и в результате она также неправильно читается. Что еще более важно, в конце программы деструктор этого string вызывается, предполагая правильную инициализацию string - и в этот момент он падает.

Ваша проблема начинается с reinterpret_cast : мой совет на этот счет таков:
1. как правило, никогда ничего не бросайте к другому типу. Если типы не совпадают, то наиболее вероятной причиной является то, что (а) ваши переменные не были определены с правильным типом для начала, или(б) переменная или функция, которую вы пытаетесь использовать с вашей переменной, не была определена с правильным типом. Постарайтесь это исправить, если это возможно.
2. Если вы должны бросить, используйте const_cast, dynamic_cast, или static_cast - в зависимости от того, что подходит.
3. Если ни одно из вышеперечисленных действий не работает, перепишите свою программу, чтобы полностью избежать приведения, или в худшем случае вам сойдет с рук случай 2. выше.

С помощью reinterpret_cast это капитуляция: вы отказываетесь от статической типизации и всех сопутствующих ей преимуществ, заменяя ее управлением байт-потоками. Это может сработать, но только если вы полностью понимаете формат фактических данных и полностью контролируете управление ими. В данном случае у вас нет ни того, ни другого.

Исходя из этого, мое предложение состоит в том, чтобы решить эту проблему в соответствии с примером 3. выше: перепишите свою программу, то есть части, которые имеют дело с записью и чтением данных. Есть и другие функции, которые полностью способны писать и читать std::string объекты, так что используйте их, а не двоичные выходные функции:

void write_sal(fstream& f, const sal& s) {
   f << s.name << endl << s.y <<endl;
}
sal read_sal(fstream& f) {
   sal s;
   f >> s.name >> s.y;
   return s;
}


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

С. П.:
Потоковый оператор operator>>(std::basic_string&) будет считываться только до первого пробела. Поэтому, если ваши входные данные могут содержать пробелы, используйте эту функцию std::basic_istream::getline() вместо:
sal read_sal(fstream& f) {
   sal s;
   std::getline(f, s.name);
   f >> s.y;
   return s;
}

Теперь, если ваш объект sal { "What do you get if you multiply six by nine?" , 42 }, то первая версия будет только читать { "What", 0 } , в то время как приведенная выше модификация будет правильно читать полный текст и номер.

П. П. С.:
Я должен упомянуть, что смешивание различных методов чтения или записи из/в потоки может вызвать проблемы, поскольку они могут использовать различные методы синхронизации. Поэтому, чтобы быть в безопасности, было бы лучше изменить эту последнюю функцию чтения следующим образом:
sal read_sal(fstream& f) {
   sal s;
   std::getline(f, s.name);
   std::string sy;
   std::getline(f, sy);
   s.y = std::stoi(sy);
   return s;
}

Видеть std:: getline - cppreference.com[^]