Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » C++ Builder || Файловый ввод/вывод

Ответить
Настройки темы
C++ Builder || Файловый ввод/вывод

Пользователь


Сообщения: 86
Благодарности: 0

Профиль | Отправить PM | Цитировать


Есть класс:

Код: Выделить весь код
class dep{
	AnsiString name;
	int Sum;
	//...
	AnsiString person;
public:
	AnsiString get_name();
	int get_sum();
	//...
}
//далее следуют описания функций-членов
//...
 
//запись в файл данных из экземпляров класса dep
//...


// считываем данные из файла
dep temp_dep;
fstream file("data.dat", ios::binary | ios::in);
file.read((char*)&temp_dep, sizeof(dep));
Form1->StringGrid1->Cells[1][1] = temp_dep.get_name{};
Form1->StringGrid1->Cells[1][1] = AnsiString(temp_dep.get_sum{});
//...{
Проблема в том, что числовые типы данных читаются из файла корректно, а вместо строк читается защищенные области памяти, т.е. вместо строки, к примеру "Гривня", читается "\x01", и т.д.
Спасибо за помощь.

Отправлено: 21:12, 02-11-2006

 

редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


Sir Z
Предлагаю вам ознакомиться с тем как представляются строки в памяти. А представляются они как правило указателем на первый символ строки и длиной строки. т.е. условно говоря строка представляет из себя
Код: Выделить весь код
class string
{
    char * data;
    size_t len;
public:
    // ...
};
Когда вы записываете свою структуру в файл (скорее всего делая fstream::write) вы записываете только 8 байт, а не саму строку. В любом случае придётся делать некий метод сериализации/десериализации.

Примерно так:
Код: Выделить весь код
class dep{
	AnsiString name;
	int Sum;
	//...
	AnsiString person;
public:
	AnsiString get_name();
	int get_sum();
	//...

	// конструктор из файла
	// можно и простым методом оформить
	dep(std::fstream &s)
	{
		s.read(&Sum, sizeof(sum));
		// и так для всех POD-данных
		size_t len;
		s.read(&len, sizeof(len));
		name.SetLength(len);
		s.read(name.data(), len);
	}

	// метод для сохранения в файл
	void serialize(std::fstream &s)
	{
		s.write(&Sum, sizeof(sum));
		// ...
		size_t len = name.Length();
		s.write(&len, sizeof(len));
		s.write(name.data(), len);
	}
}
P.S. Нелогично использовать борландовский AnsiString со стандартными файловыми потоками. Перейдите либо на связку std::string/std::fstream, либо на AnsiString/TFileStream, будет смотреться всё гораздо более цельно.

-------
http://ivank.ru


Последний раз редактировалось ivank, 04-11-2006 в 17:19.


Отправлено: 23:15, 02-11-2006 | #2



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

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


Пользователь


Сообщения: 86
Благодарности: 0

Профиль | Отправить PM | Цитировать


Предложенный Вами метод вообще исключает логическую связность хранимых данных, для чего был создан класс dep. Если идти таким путем, гораздо легче записывать подряд название, сумму, имя человека, название, сумму, имя человека и т.д. Вряд ли это оптимальный вариант в данной ситуации.
Подчеркну, что данные предпочтительно хранить именно в AnsiString вследствие внутреннего строения большинства компонентов vcl. Неужели крутые программеры, записывая данные из класса в файл, вручную переносят значения ВСЕХ переменных?

Отправлено: 01:08, 04-11-2006 | #3


Аватара для hasherfrog

Старый параноик


Сообщения: 2423
Благодарности: 85

Профиль | Отправить PM | Цитировать


Sir Z
Почитайте чего-нибудь про сериализацию классов. В гугле есть.

>> Неужели крутые программеры, записывая данные из класса в файл, вручную переносят значения ВСЕХ переменных?

Ох, не могу не похвалиться... Написал (уже полностью) библиотеку на QT, которая любой класс обучает самосериализации. Типа берёшь класс, добавляешь интерфейс спецмакросом, в спецметоде описываешь регистрацию переменных для выброса-вброса в XML - и всё. В любой момент ррраз - и класс пишет или читает себя и всех вложенных детей (в том числе и других таких же классов) в файл :-) Поддерживаются даже массивы.

Дикая смесь шаблонов, макросов и наследований... :-)
Мой лучший (пока) в жизни системный продуктик...

Отправлено: 01:18, 04-11-2006 | #4


редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


Sir Z
Цитата:
Предложенный Вами метод вообще исключает логическую связность хранимых данных, для чего был создан класс dep.
Логическая связность достигается тем, что сохранением и загрузкой занимаются всего 2 метода. Остальным про то в каком формате сохраняются данные ничего знать не надо.

Сейчас сериализацию модно автоматически (т.е. без написания своего кода) с помощью reflection делать. Но это в .NET/Java + скриптовых языках. На C++ не проканает.

На C++ можно действительно можно пользоваться макросами для автонаписания подобного кода. Или boost::serialization, который вполне вероятно войдёт в следующий стандарт C++.

hasherfrog
А я подобную штуку (макросы для автомериализации) ещё лет 5 назад писал, в качестве proof of concept. Правда в голые файлы сохранял всё. Не любил XML, да и не люблю до сих пор. К сожалению, моя первая дом. страница закрылась раньше, чем её проиндексировал webarchive.org Все свои поделки я выкладывал именно там. А теперь ведь и взять больше неоткуда того чуда, дабы над ним посмеяться

-------
http://ivank.ru


Отправлено: 02:21, 04-11-2006 | #5

pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Sir Z, честно говоря, я не совсем понял, чего вы хотите добиться. Из того что вами написано, могу только поддержать ответивших вам форумчан. Давайте поясню:
Код: Выделить весь код
class dep{
	AnsiString name; 
	int Sum;
   // класс, который содержит имя, сумму и проч. в КАЖДОМ экзеспляре
   // причём тут связность или несвязность?
public:
   // здесь ivank добавил методы, которые безопасно выполняют то,
   // что вы довольно жестоко делаете в разделе "считываем данные из файла"
}

// считываем данные из файла
dep temp_dep;
fstream file("data.dat", ios::binary | ios::in);
// БЕЗОПАСНОСТЬ ТИПОВ ДАННЫХ - это то, из-за чего отказались от C и придумали C++
file.read((char*)&temp_dep, sizeof(dep)); // вы сейчас память покалечили, это и хотел сказать ivank
// кроме того, iostream - для работы с данными в текстовом виде, а не дампа памяти

Form1->StringGrid1->Cells[1][1] = temp_dep.get_name{}; // зачем это было делать?
Form1->StringGrid1->Cells[1][1] = AnsiString(temp_dep.get_sum{}); // всё равно поверх записали что-то
//...{
Давайте так (если вы действительно хотите разобраться). Напишите цель: что должно быть в файле и что вы хотите сделать (пример)? Я думаю ответ будет исчерпывающим.

Отправлено: 11:04, 04-11-2006 | #6


Пользователь


Сообщения: 86
Благодарности: 0

Профиль | Отправить PM | Цитировать


pva:
Давайте по порядку.
1. Когда я говорил о связности, я говорил о том, что гораздо удобнее читать весь объект сразу, а не его члены по отдельности. И так же записывать.
2.
Цитата:
что вы довольно жестоко делаете в разделе "считываем данные из файла"
что я довольно жестоко делаю в разделе "считываем данные из файла"?
Цитата:
file.read((char*)&temp_dep, sizeof(dep)); // вы сейчас память покалечили, это и хотел сказать ivank
Дело в том, что именно такой способ освещен в книге товарища Владимира Шамиса. В чем здесь заключается ошибка? Что, получается, что когда дело доходит до записи в файл переменной AnsiString, копируется адрес строки, а сама строка не копируется? Почему корректный вывод в файл AnsiString'ов не происходит автоматически, в то время как чтение этих строк происходит без пробем? если весь объект записывается целиком (в смысле, записываются и данные, и их адреса в памяти), то почему тогда данные int читаются верно?
Цитата:
iostream - для работы с данными в текстовом виде, а не дампа памяти
а это вообще интересно... а куда же тогда читать из файла, если не в память? А как же ios::binary? это разработчики ошиблись, нельзя так?
Цитата:
Form1->StringGrid1->Cells[1][1] = temp_dep.get_name{}; // зачем это было делать?
Form1->StringGrid1->Cells[1][1] = AnsiString(temp_dep.get_sum{}); // всё равно поверх записали что-то
//...
}
Поверьте, там все нормально, конечно, координаты ячейки там разные, запарился...

ЦЕЛЬ: есть класс, представляющий, к примеру, человека. Человек должно быть несколько(разных) и этот список надо хранить в файле. Данные хотелось бы защитить от чтения вне программы, поэтому читаю и пишу бинарно. Кстати, где достать классы шифрования данных?

Отправлено: 15:26, 04-11-2006 | #7


редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


Sir Z
Цитата:
Дело в том, что именно такой способ освещен в книге товарища Владимира Шамиса. В чем здесь заключается ошибка? Что, получается, что когда дело доходит до записи в файл переменной AnsiString, копируется адрес строки, а сама строка не копируется? Почему корректный вывод в файл AnsiString'ов не происходит автоматически, в то время как чтение этих строк происходит без пробем? если весь объект записывается целиком (в смысле, записываются и данные, и их адреса в памяти), то почему тогда данные int читаются верно?
(чтение тоже не происходит корректного)

Вы знаете что такое указатель? А C-строки? Давайте ещё попробую объяснить всё ещё более на пальцах, чем до этого.

C-строка - это указатель на первый элемент строки. Строка это просто последовательность символов (char), заканчивающаяся символом '\0'.
Код: Выделить весь код
char *str = (char*)malloc(100); //выделили 100 байт памяти
// предположим, что str указывает на ячейку 0xdecafbad
strcpy(str, "some string"); //записали в строку "some string" по адресу 0xdecafbad
outfile.write(&str, sizeof(str)); // записываем четыере байта - только указатель
free(str); // освобождаем памяти
// Таким образом в файле - 4 байта - 0xdecafbad, а не требумая нам строка

// ....
infile.read(&str, sizeof(str)); // считали 4 байта
// Теперь str=0xdecafbad. но память по этому адресу ещё не выделена
// и тем более ничем не инициализирована
// Проще говоря str указывает на мусор, со всеми вытекающими
Так вот, AnsiString по сути своей - обёртка вокруг сишных строк. То бишь, в поле данных у AnsiString есть только один указатель на строку. Сама строка находится "вне" области памяти отводимой под экземпляр AnsiString. Соответственно "записывая" строку таким образом, как вы, мы записываем только указатель на саму строку. Когда мы строку "считываем", мы считываем указатель, который указывает в никуда. Со всеми вытекающими.

P.S. записывать любые дынные сложнее, чем простые встроенные типы (int, char, double итп) банальным дампом памяти в двоичный файл нельзя. Да и то, даже с простыми типами будут появляться проблемы с переносом на различные архитектуры, ибо размер int может быть как 2 байта, так и 4 и 8. И порядок следования байтов может быть на разных платформах различный. Текстовые форматы надёжней.

Цитата:
Данные хотелось бы защитить от чтения вне программы, поэтому читаю и пишу бинарно.
Бред. Такую защиту любой школьник взломает.

Цитата:
Кстати, где достать классы шифрования данных?
google?

-------
http://ivank.ru


Отправлено: 17:41, 04-11-2006 | #8



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » C++ Builder || Файловый ввод/вывод

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
C/C++ - [решено] Файловый ввод Lisiy_egik Программирование и базы данных 1 20-01-2010 05:40
Debian/Ubuntu - Файловый сервер Joni Общий по Linux 4 02-12-2008 14:29
Основы C++ | Файловый ввод Skrip Программирование и базы данных 12 05-08-2005 18:52
файловый менджеры rblack Программное обеспечение Windows 7 10-11-2004 15:17
Файловый ввод-вывод в ring0 wano Программирование и базы данных 13 12-11-2002 18:21




 
Переход