Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   C++ Builder || Файловый ввод/вывод (http://forum.oszone.net/showthread.php?t=73931)

Sir Z 02-11-2006 21:12 506041

C++ Builder || Файловый ввод/вывод
 
Есть класс:

Код:

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", и т.д.
Спасибо за помощь.

ivank 02-11-2006 23:15 506077

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, будет смотреться всё гораздо более цельно.

Sir Z 04-11-2006 01:08 506703

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

hasherfrog 04-11-2006 01:18 506707

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

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

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

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

ivank 04-11-2006 02:21 506718

Sir Z
Цитата:

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

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

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

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

pva 04-11-2006 11:04 506802

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{}); // всё равно поверх записали что-то
//...{

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

Sir Z 04-11-2006 15:26 506926

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{}); // всё равно поверх записали что-то
//...
}
Поверьте, там все нормально, конечно, координаты ячейки там разные, запарился...

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

ivank 04-11-2006 17:41 506967

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?


Время: 10:20.

Время: 10:20.
© OSzone.net 2001-