Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Сохранение в файл текущей задачи


Автор: Нитонисе 16.11.2011, 09:32
Задача такая - нужно сохранить текущую работу в файл. Наподобие как в Word можно сохранить файл с набранным текстом. То есть на выходе должен получаться файл с зарезервированной иконкой и ассойиированный с моим приложениме. В последствии двойной клик в проводнике по этому файлу - и все его содержимое загружается в программу.

Но вопрос не в том, как сделать иконку и установить ассоциативню связь с моим приложением, а в том как организовать хранение всех переменных в файле.

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

Для примера. Допустим есть такие управляющие элементы:

"Поле для ввода А".
"Поле для ввода Б".
"Поле для ввода В".
"Переключатель".
"Таблица" (в таблице 5 колонок).
Например в текущей работе такое состояние:
Текст в поле А - "текстА".
Текст в поле Б - "текстБ".
Текст в поле В - "текстВ".
Значение переключателя - true.
В таблице 3 записи:
первая запись - "1кол1,1кол2,1кол3,1кол4,1кол5"
вторая запись - "2кол1,2кол2,2кол3,2кол4,2кол5"
третья запись - "3кол1,3кол2,3кол3,3кол4,3кол5"

Вот нужно все это записать в файл и затем загрузить из него, записав все значения полей, таблиц и запомнив состояние переключателя. 

Автор: Данкинг 16.11.2011, 10:13
Так выводи данные из контролов в в файл в определённом порядке. А затем - при обратной загрузке из файла - значение из соответствующей строчки подставляй соответствующему контролу.

Автор: Нитонисе 16.11.2011, 11:30
Цитата(Данкинг @  16.11.2011,  10:13 Найти цитируемый пост)
Так выводи данные из контролов в в файл в определённом порядке. А затем - при обратной загрузке из файла - значение из соответствующей строчки подставляй соответствующему контролу.

Это слишком прямолинейное и неудобное решение. Можно ли как-то организовать файл, чтобы обращаться к тому или иному значению можно было по переменной? То есть чтобы загрузить некое значение из файла в поле, скажем Edit1->Text, мне нужно было знать не порядковый номер этой строки в файле, а имя некоей переменной. Например Edit1->Text = File->MyEdit. Что-то тмпо того.
Хранение просто построчно всей информации неудобно, потому что если я сдвину строки в файле, то придется заново переписовать функции загрузки и сохранения. А если обращение идет по неким переменным, то ничего и переписывать не надо.

Автор: vol4ek 16.11.2011, 11:36
используй INI файл. WritePrivateProfileString() и GetPrivateProfileString()

Добавлено через 25 секунд
очень удобно

Автор: Данкинг 16.11.2011, 11:41
Ну, тогда действительно INI-файл. Секция - переменная - значение.

Автор: kin 16.11.2011, 11:45
Привет! На мой взгляд, все до безобразия просто: посмотри как организован проект у того же Buildera или VS. Это XML-файл (или что-то подобное) где каждый узел - это отдельный элемент твоей работы, атрибуты этого узла (или его дочерние узлы) - свойства элемента. Ну а в чем плюсы(минусы) такой конструкции разберешься сам.

Автор: vol4ek 16.11.2011, 11:48
слишком мудрено. В INI указал нужный ключ и получил данные. А так можно и в реестр настройки совать.

Автор: Нитонисе 16.11.2011, 12:07
ini вроде как выглядит неплохо, только я пока не разбирался в детальном принципе работы. Но тут есть вот еще какая заковыка. Файл должен быть с моим расширением. Например "файл.my". Как можно его обрабатывать как ini-файл?

Автор: vol4ek 16.11.2011, 12:17
запросто. любое расширение.

Добавлено через 2 минуты и 24 секунды
Код

#include <windows.h>


//Функция GetPrivateProfileString, как не трудно догадаться по названию, служит для чтения данных из INI-файлов. Вот как выглядит ее вызов:


DWORD WINAPI GetPrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
LPCTSTR lpDefault,
LPTSTR lpReturnedString,
DWORD nSize,
LPCTSTR lpFileName);

//lpAppName — имя секции, в которой находится считываемй ключ. Если в качестве этого параметра передать NULL, функция вернет список всех секций в указанном INI-файле
//lpKeyName — имя ключа, чье значение нужно считать. Если в качестве параметра передать NULL, то функция, по аналогии с секциями, вернет имена всех ключей в секции.
//lpDefault — строка по умолчанию. Если в указанном INI-файле ключ lpKeyName не найден, то функция вернет это значение.
//lpReturnedString — буфер, в который будут записаны возвращаемые функцией данные
//lpFileName — имя считываемого INI-файла.

//Функция WritePrivateProfileString служит для записи данных в INI-файлы 


BOOL WINAPI WritePrivateProfileString(
LPCTSTR lpAppName,
LPCTSTR lpKeyName,
LPCTSTR lpString,
LPCTSTR lpFileName);




//lpAppName — имя секции, в которую нужно записать значение. Если секции с таким именем не существует, она будет создана. Имя секции регистронезависимо.
//lpKeyName — имя ключа, ассоциируемого со значением. Если такого ключа нет, он будет создан автоматически. Внимание! Если в качестве имени ключа передать NULL, будет удалена вся секция lpAppName.
//lpString — строка, которую следует записать в INI-файл. Если в качестве этой строки передать NULL, то ключ lpKeyName будет удален.


Добавлено через 7 минут и 22 секунды
одно уточнение.
Код

WritePrivateProfileString("имя секции","ключ секции","данные","My.file");


если в последнем параметре указано имя файла без пути, по умолчанию файл создается в папке Windows.

Автор: kin 16.11.2011, 12:26
Цитата(vol4ek @  16.11.2011,  11:48 Найти цитируемый пост)
слишком мудрено. В INI указал нужный ключ и получил данные. 
 Не спорю, все зависит от задачи. На мой взгляд, INI-файл  хорошо работает, когда система плоская, а если есть иерархия тут, все же, XML сподручнее будет.

Автор: Нитонисе 16.11.2011, 12:35
А как решить задачу хранения данных таблицы в ini? Когда есть набор статичных полей для записи или чекбоксы - то все понятно, каждому контролу - своя запись в ini. А в таблице может быть несколько строчек. Как организовать хранение табличных данных в ini?

И еще, я так понял расширение могу давать файлу любое, но работать с ним как с ini и все будет ОК?

Автор: vol4ek 16.11.2011, 12:55
будет ок. Какое расширение задано в функциях, с тем расширением и будет работать.
хранение таблицы. Ну например так:

Код

char * st = new char[1]; // здесь имя столбца
char * str = new char[1]; // здесь номер строки
char * buf = new char[1]; // здесь содержимое
WritePrivateProfileString(st,str,buf,"C:\\твой файл.расширение");


Автор: Нитонисе 16.11.2011, 13:08
Еще вопрос. Можно ли как-то зашифровать этот файл при записи? Чтобы его нельзя было просматривать например в блокноте. Соответственно перед считыванием информации - расшифровать.

Автор: vol4ek 16.11.2011, 13:20
не скажу не знаю. файл текстовый, блокнотом просматривается, выглядеть будет так:

[секция]
ключ1 = значение
ключ2 = значение
[секция2]
ключ1 = значение

и т.д.

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

Добавлено через 1 минуту и 1 секунду
хотя мудрено. а что там у тебя лежать-то будет?

Автор: Нитонисе 16.11.2011, 13:25
Обычный файл. В нем ничего секретного. Просто хочу лишить пользователя возможности править файл вручную, а то может что-то некорректно подправить. Например некое поле не может принимать значение больше 100. Пользователь в файле выставит 200 - вот и получится некорректный итог программы.

Автор: vol4ek 16.11.2011, 13:31
дай файлу расширение системное com,exe,dll и спрячь куда нибудь в Windows\\system32 например. Не забудь указать путь в GetPrivateProfileString(). Создать свою папку можно так CreateDirectory("путь\\имя",NULL);

Автор: Данкинг 16.11.2011, 13:31
Нитонисе, ещё извращённый способ: хранить настройки в .dbf. И таблица, и редактировать "простому смертному" не дано.

Автор: vol4ek 16.11.2011, 13:33
Цитата(Данкинг @ 16.11.2011,  13:31)
Нитонисе, ещё извращённый способ: хранить настройки в .dbf. И таблица, и редактировать "простому смертному" не дано.

мы не ищем легких путей. smile 

Автор: Нитонисе 16.11.2011, 13:51
Цитата(vol4ek @  16.11.2011,  13:31 Найти цитируемый пост)
дай файлу расширение системное com,exe,dll и спрячь куда нибудь в Windows\\system32 например.

Не, файл прятать не нужно. По сути файл - это проект пользователя. Как отдельный файл Word например. Этот файл можно передать другому пользователю и он может посмотреть работу первого пользователя.

Автор: Alca 16.11.2011, 14:08
MDB

Автор: Данкинг 16.11.2011, 14:27
Цитата(vol4ek @  16.11.2011,  14:33 Найти цитируемый пост)
мы не ищем легких путей.

Вообще я бы так и сделал, особенно если сохраняемых настроек много. smile

Добавлено через 33 секунды
Цитата(Alca @ 16.11.2011,  15:08)
MDB

Или даже так. 

Автор: xvr 16.11.2011, 14:34
Цитата(Нитонисе @  16.11.2011,  09:32 Найти цитируемый пост)
Вот нужно все это записать в файл и затем загрузить из него, записав все значения полей, таблиц и запомнив состояние переключателя.

Обойти форму (проперти Components и ComponentsCount кажется) и сбросить содержимое контролов (имя брать из проперти Name, а вот значение придется вытаскивать вручную для каждого типа контрола).
Сбрасывать можно как в ini файл, так и в xml - как удобнее
Цитата(Нитонисе @  16.11.2011,  13:25 Найти цитируемый пост)
Просто хочу лишить пользователя возможности править файл вручную,

Подписать файл. Например посчитать его MD5 и записать в конец

Автор: ZVano 24.11.2011, 10:28
Цитата(Нитонисе @  16.11.2011,  13:25 Найти цитируемый пост)
Просто хочу лишить пользователя возможности править файл вручную

Можно воспользоваться BASE64 кодированием значений.
API: CryptStringToBinary и CryptBinaryToString.

У меня их вызовы обернуты в библиотеку. Не очень удачный вариант кода, но все же лучше чем ничего.

myLib.h
Код

    namespace winapi {
        namespace wincrypt {
            enum TCryptStringMode {
                CSM_BASE64HEADER // CRYPT_STRING_BASE64HEADER           0x00000000
                , CSM_BASE64 // CRYPT_STRING_BASE64                 0x00000001
                , CSM_BINARY // CRYPT_STRING_BINARY                 0x00000002
                , CSM_BASE64REQUESTHEADER // CRYPT_STRING_BASE64REQUESTHEADER    0x00000003
                , CSM_HEX // CRYPT_STRING_HEX                    0x00000004
                , CSM_HEXASCII // CRYPT_STRING_HEXASCII               0x00000005
                , CSM_BASE64_ANY // CRYPT_STRING_BASE64_ANY             0x00000006
                , CSM_ANY // CRYPT_STRING_ANY                    0x00000007
                , CSM_HEX_ANY // CRYPT_STRING_HEX_ANY                0x00000008
                , CSM_BASE64X509CRLHEADER // CRYPT_STRING_BASE64X509CRLHEADER    0x00000009
                , CSM_HEXADDR // CRYPT_STRING_HEXADDR                0x0000000a
                , CSM_HEXASCIIADDR // CRYPT_STRING_HEXASCIIADDR           0x0000000b
                , CSM_HEXRAW // CRYPT_STRING_HEXRAW                 0x0000000c
                , CSM_HASHDATA // CRYPT_STRING_HASHDATA               0x10000000
                , CSM_STRICT // CRYPT_STRING_STRICT                 0x20000000
                , CSM_NOCRLF // CRYPT_STRING_NOCRLF                 0x40000000
                , CSM_NOCR // CRYPT_STRING_NOCR                   0x80000000
            };
            extern DWORD GetCodeCryptStringMode(TCryptStringMode mode);

            extern bool API_CryptBinaryToString(RawByteString data, TCryptStringMode mode, AnsiString *out_result);
            extern bool API_CryptStringToBinary(RawByteString data, TCryptStringMode mode, AnsiString *out_result);
        };
    };


myLib.cpp
Код

    namespace winapi {
        namespace wincrypt {
            DWORD GetCodeCryptStringMode(TCryptStringMode mode) {
                DWORD result = 0;

                switch(mode) {
                case CSM_BASE64HEADER:
                    result = CRYPT_STRING_BASE64HEADER;
                    break;
                case CSM_BASE64:
                    result = CRYPT_STRING_BASE64;
                    break;
                case CSM_BINARY:
                    result = CRYPT_STRING_BINARY;
                    break;
                case CSM_BASE64REQUESTHEADER:
                    result = CRYPT_STRING_BASE64REQUESTHEADER;
                    break;
                case CSM_HEX:
                    result = CRYPT_STRING_HEX;
                    break;
                case CSM_HEXASCII:
                    result = CRYPT_STRING_HEXASCII;
                    break;
                case CSM_BASE64_ANY:
                    result = CRYPT_STRING_BASE64_ANY;
                    break;
                case CSM_ANY:
                    result = CRYPT_STRING_ANY;
                    break;
                case CSM_HEX_ANY:
                    result = CRYPT_STRING_HEX_ANY;
                    break;
                case CSM_BASE64X509CRLHEADER:
                    result = CRYPT_STRING_BASE64X509CRLHEADER;
                    break;
                case CSM_HEXADDR:
                    result = CRYPT_STRING_HEXADDR;
                    break;
                case CSM_HEXASCIIADDR:
                    result = CRYPT_STRING_HEXASCIIADDR;
                    break;
                case CSM_HEXRAW:
                    result = CRYPT_STRING_HEXRAW;
                    break;
                case CSM_HASHDATA:
                    result = CRYPT_STRING_HASHDATA;
                    break;
                case CSM_STRICT:
                    result = CRYPT_STRING_STRICT;
                    break;
                case CSM_NOCRLF:
                    result = CRYPT_STRING_NOCRLF;
                    break;
                case CSM_NOCR:
                    result = CRYPT_STRING_NOCR;
                    break;
                default: ;
                }
                return result;
            }

            bool API_CryptBinaryToString(RawByteString data, TCryptStringMode mode, AnsiString *out_result) {
                DWORD result_size;
                BOOL api_result;
                AnsiString result;
                DWORD modeDW = GetCodeCryptStringMode(mode);

                api_result = CryptBinaryToString((unsigned char*)data.c_str(), data.Length(), modeDW, NULL, &result_size);
                if (!api_result) {
                    return false;
                }
                char *mem_data = NULL;
                mem_data = (char*)GlobalAlloc(GPTR, result_size);
                try {
                    api_result = CryptBinaryToString((unsigned char*)data.c_str(), data.Length(), modeDW, mem_data, &result_size);
                    if (!api_result) {
                        return false;
                    }
                    result = AnsiString(mem_data);
                }
                __finally {
                    GlobalFree(mem_data);
                }
                result = result.Delete(result_size - 1, 2);

                *out_result = result;
                return true;
            }

            bool API_CryptStringToBinary(RawByteString data, TCryptStringMode mode, AnsiString *out_result) {

                BOOL api_result;
                DWORD result_size;
                AnsiString result;
                DWORD modeDW = GetCodeCryptStringMode(mode);

                api_result = CryptStringToBinary(data.c_str(), data.Length(), modeDW, NULL, &result_size, NULL, NULL);
                if (!api_result) {
                    return false;
                }
                char *r;
                r = (char*)GlobalAlloc(GPTR, result_size);
                try {
                    api_result = CryptStringToBinary(data.c_str(), data.Length(), modeDW, r, &result_size, NULL, NULL);
                    if (!api_result) {
                        return false;
                    }
                    result = AnsiString(r);
                    result.SetLength(result_size);
                }
                __finally {
                    GlobalFree(r);
                }

                *out_result = result;
                return true;
            }
        }
    }


Используем например так:
Код

        /*Кодирует строку в формат BASE-64*/
        UnicodeString UnicodeStringToBase64(UnicodeString data, int newlinePos){
            AnsiString result;
            AnsiString dataA = data;
            winapi::wincrypt::API_CryptBinaryToString(dataA, winapi::wincrypt::CSM_BASE64, &result);
            result = StringReplace(result, "\r\n", "", TReplaceFlags() << rfReplaceAll);
            result = StringReplace(result, "\n", "", TReplaceFlags() << rfReplaceAll);
            if (newlinePos > 0) {
                int pos = 1;
                RawByteString wrap_result;
                while (true){
                    if (pos > result.Length()) {
                        break;
                    }
                    wrap_result = wrap_result + result.SubString(pos, newlinePos) + "\r\n";
                    pos += newlinePos;
                }
                result = wrap_result;
            }
            return result;
        }

        /*Декодирует строку из формата BASE-64*/
        UnicodeString Base64ToUnicodeString(UnicodeString data){
            AnsiString dataA = data;
            AnsiString result;
            winapi::wincrypt::API_CryptStringToBinary(dataA, winapi::wincrypt::CSM_BASE64, &result);
            return result;
        }

Автор: XPyCT 25.11.2011, 08:48
Все гораздо и гораздо проще.

1 создаешь структуру
2.Вектор
3. #include<stdio.h>, #include<vector> 

и сохраняешь и читаешь все что тебе нужно из вектора

сохраняешь в бинарный файл расширение дат или какое тебе нужно

лично читаю файлы на ура такого рода как приложен в аттаче

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)