Сохраняем настройки в INI-файлах
Большинство программистов сталкиваются с проблемой сохранения настроек своей программы, но сегодня мы решим эту проблему. Мы научимся работать с ini файлами. Еще одним способом сохранять настройки является реестр, но его лучше не изменять, да и работать с ним немного сложнее.
Итак, ini-файл имеет простую и понятную структуру :
[Секция1]
Идентификатор1 = Значение1
Идентификатор2 = Значение2
[Секция2]
Идентификатор1 = Значение1
Для использования ini-файлов, в
C++Builder предусмотрен модуль
IniFiles , его мы и подключаем:
C++ |
1
| #include "IniFiles.hpp" |
|
Далее, нужно создать обьект класса
TIniFile, назовем его
Ini.
После
пишем:
C++ |
1
| TIniFile *Ini = new TIniFile("c:/options.ini"); |
|
значения сохраняет на диске С: в файл
options.ini .
После этого, на форму кинем две кнопки (
TButton) и два Эдита (
TEdit). Мы будем сохранять текст Эдитов в ini-файл, а потом загружать его.
Пишем обработчик события нажатия кнопки
Button1. Она будет сохранять текст Эдитов в наш
options.ini
C++ |
1
2
3
4
5
| void __fastcall TForm1::Button1Click(TObject *Sender)
{
Ini->WriteString("Edits","Edit1",Edit1->Text);
Ini->WriteString("Edits","Edit2",Edit2->Text);
} |
|
Так, как мы работаем с текстом, мы использовали метод класса WriteString,
если нам нужно было бы работать с числами, мы бы написали WriteInteger .
Где параметры для этой функции:
"Edits" - имя секции;
"Edit1" - указатель на объект;
"Edit1->Text" - строка, которую мы сохраняем (текст эдита).
Далее, рассмотрим пример загрузки параметров с ini , пишем обработчик события для второй кнопки:
C++ |
1
2
3
4
5
| void __fastcall TForm1::Button2Click(TObject *Sender)
{
Edit1->Text = Ini->ReadString("Edits","Edit1","Def");
Edit2->Text = Ini->ReadString("Edits","Edit2","Def");
} |
|
Здесь используем функцию чтения :ReadString , где:
"Edits" - секция из которая нам надо ;
"Edit1" - объект для которого загружаем строку;
"Def" - эта строка заполняет Эдит, если значение ini не найдено;
Этот маленький пример показывает как можно лекго загружать и сохранять параметры программы в INI-файлы.
Методы и свойства реализуемые TIniFile
Использовать или установить это ... Чтобы сделать это ... Наследование
Код
Create Сконструировать экземпляр TIniFile. TObject
DeleteKey Удалить идентификатор INI-файла и его значение. Нет
EraseSection Убрать из INI-файла целый раздел. Нет
FileName Получить инкапсулированное имя INI-файла. Нет
ReadBool Извлечь из INI-файла логическое значение. Нет
Readinteger Извлечь из INI-файла целое значение. Нет
ReadSection Извлечь из раздела INI-файла имена идентификаторов. Нет
ReadSections Извлечь из INI-файла список имен разделов. Нет
ReadSectionValues Извлечь из INI-файла все содержимое раздела. Нет
ReadString Извлечь из INI-файла строковое значение. Нет
WriteBool Записать в INI-файл логическое значение. Нет
Writeinteger Записать в INI-файл целое значение Нет
WriteString Записать в INI-файл строковое значение Нет
Важно
Методы
ТIniFile будут пытаться найти INI-файл в каталоге
Windows. Если вы хотите читать или писать INI-файл, расположенный не в каталоге Windows, следует явно добавить путь к имени INI-файла при его открытии в
Create.
Автозагрузка конфигурации
Создаем 2 кнопки, раздела меню, т.д которые будут отвечать за запись файла, чтение файла конфигурации.
Код примерно такой (для 2 CheckBox'ов):
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // SAVE
void __fastcall TForm1::Button1Click(TObject *Sender){
AnsiString FileName = ExtractFilePath( Application->ExeName ) + "config.dat";
ofstream fout( FileName.c_str() );
if( fout ){
fout << CheckBox1->Checked << endl;
fout << CheckBox2->Checked << endl;
fout.close();
}
}
// LOAD
void __fastcall TForm1::Button2Click(TObject *Sender){
AnsiString FileName = ExtractFilePath( Application->ExeName ) + "config.dat";
ifstream fin( FileName.c_str() );
bool check = false;
if( fin ){
CheckBox1->Checked = ( fin >> check, check );
CheckBox2->Checked = ( fin >> check, check );
fin.close();
}
} |
|
Теперь для записи конфигурации нужно вызвать функцию
для чтения
C++ |
1
| Button2Click( Sender ); |
|
Например чтобы при закрытии приложения конфигурация автомитически сохранялась, надо написать обработчик события OnClose для формы.
C++ |
1
2
3
| void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action){
Button1Click( Sender );
} |
|
Чтобы автоматически загружалась - обработчик OnCreate
C++ |
1
2
3
| void __fastcall TForm1::FormCreate(TObject *Sender){
Button2Click( Sender );
} |
|
для работы ofstream и ifstream необходимо включить заголовочный файл
Обработка события изменения CheckBox - OnClick
C++ |
1
2
3
4
5
6
7
| void __fastcall TForm1::CheckBox1Click(TObject *Sender){
if( CheckBox1->Checked ){
//...
} else{
//...
}
} |
|
Сделать структуру, например, в .h-файле, такого вида:
C++ |
1
2
3
4
| struct TParameters
{
bool IfFormatCOnUpload; // всякие параметры, например, - булевские переменные
}; |
|
Создать объект этой структуры.
Например, в FormDestroy все писать в реестр, в Ini-файл или в какой-нибудь бинарный, а при создании окна все загружать и проставлять индикаторы.
При изменении параметры структуры тоже изменять или же смотреть все в самом конце.
Чтобы узнать последнюю просматривавшуюся, надо в OnChange у комбобокса написать:
C++ |
1
| LastIndex = ComboBox1->ItemIndex; |
|
Ini-объект можно просто оздать в начале, удалил в конце.
Код с комментариями.
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| TIniFile * Ini; // объект .ini-файла
void __fastcall TForm1::WriteParams() // запись всех значений в .ini-файл
{
Ini->WriteInteger ( "Form1", "Form1Width", Width ); // ширина
Ini->WriteInteger ( "Form1", "Form1Height", Height ); // высота
Ini->WriteInteger ( "Form1", "Form1Top", Top ); // вершина
Ini->WriteInteger ( "Form1", "Form1Left", Left ); // лево
Ini->WriteString ( "Form1", "Form1Caption", Caption ); // заголовок
Ini->WriteInteger ( "Form1", "ComboBox1ItemIndex", ComboBox1->ItemIndex); // индекс
Ini->WriteBool ( "Form1", "CheckBox1Checked", CheckBox1->Checked ); // состояние 1
Ini->WriteBool ( "Form1", "CheckBox2Checked", CheckBox2->Checked ); // состояние 2
Ini->WriteBool ( "Form1", "InitMax", WindowState == wsMaximized ); // вид формы
Ini->WriteString ( "Memos", "Form1Memo1", Memo1->Text ); // содержимое
Ini->UpdateFile(); // это обязательно, чтобы заново переписался файл
}
void __fastcall TForm1::ReadParams()
{
Width = Ini->ReadInteger( "Form1", "Form1Width", 800 ); // ширина
Height = Ini->ReadInteger( "Form1", "Form1Height", 600 ); // высота
Top = Ini->ReadInteger( "Form1", "Form1Top", 100 ); // вершина
Left = Ini->ReadInteger( "Form1", "Form1Left", 200 ); // лево
Caption = Ini->ReadString ( "Form1", "Form1Caption", "MyProg"); // заголовок
ComboBox1->ItemIndex = Ini->ReadInteger( "Form1", "ComboBox1ItemIndex", 0 ); // индекс
CheckBox1->Checked = Ini->ReadBool ( "Form1", "CheckBox1Checked", true ); // состояние 1
CheckBox2->Checked = Ini->ReadBool ( "Form1", "CheckBox2Checked", true ); // состояние 2
WindowState = Ini->ReadBool ( "Form1", "InitMax", false) ? wsMaximized : wsNormal;
Memo1->Text = Ini->ReadString ( "Memos", "Form1Memo1", "" ); // содержимое
}
void __fastcall TForm1::FormCreate(TObject * Sender) // создание формы
{
Ini = new TIniFile(ExtractFilePath(Application->ExeName) + "mytestini.ini"); // создаем объект
ReadParams(); // считываем все в поля из файла
}
void __fastcall TForm1::FormDestroy(TObject * Sender)
{
WriteParams(); // записываем все в последний раз
delete Ini; // удаляем объект
}
void __fastcall TForm1::BtnSaveClick(TObject * Sender)
{ WriteParams(); } // сохранение при нажатии кнопки
void __fastcall TForm1::BtnLoadClick(TObject * Sender)
{ ReadParams(); } // загрузка при нажатии кнопки |
|
Чтобы не приучатся писать лишнего кода достаточно объявить функции
C++ |
1
2
| void __fastcall ReadParams( TObject *Sender );
void __fastcall WriteParams( TObject *Sender ); |
|
в части класса формы
и потом в инсперторе объектов для BtnLoad обработчиком события выбрать
ReadParams.
Для Save аналогично.
Можно, но очень даже возможно, что при нажатии этих кнопок кроме как загружать данные из файла, надо еще что-то делать, поэтому, и чтобы не запутаться, это выделено в разные места.
Запись данных из файла INI в Memo
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| void __fastcall TForm1::Button1Click(TObject *Sender)
{
TIniFile *ini = new TIniFile(ExtractFileDir(Application->ExeName) + "\\MyFile.ini");
TStringList* Sections = new TStringList;
TStringList* Values = new TStringList;
if (ini) {
ini->ReadSections(Sections);
for (int i=0; i<Sections->Count; i++) {
Memo1->Lines->Add("--------------\r\n" + Sections->Strings[i] + "\r\n--------------");
ini->ReadSectionValues(Sections->Strings[i], Values);
for (int j=0; j<Values->Count; j++) Memo1->Lines->Add(Values->Strings[j]);
}
delete ini;
}
delete Values;
delete Sections;
} |
|
Как создать Ini-файл в директории программы
По умолчанию ini-файл создается в Windows-директории (например:
TIniFile.Create('FOO.INI' )), что приводит к "захламлению" оной. Более (эко-)логично (за исключением случаев, когда программа делается для CD-ROM) если ini-файл создается в той же директории что и главная программа. Это достигается с помощью одной строки:
C++ |
1
| IniFile := TIniFile.Create(ChangeFileExt(ParamStr(0),'.INI')); |
|
Как сохранить FontDlg->Style в ini-файле
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| TFont *CaptionFont = new TFont();
//Параметры стиля запоминается как bool значения
//Установку параметра шрифта проверяем через Contains
//Запись параметров шрифта
ini->WriteString(Key, "CFN", CaptionFont->Name);//
ini->WriteInteger(Key, "CFS", CaptionFont->Size);//
ini->WriteInteger(Key, "CFC", (int)CaptionFont->Color);//
ini->WriteInteger(Key, "CFE", (int)CaptionFont->Charset);//
ini->WriteBool(Key, "CFB", CaptionFont->Style.Contains(fsBold));
ini->WriteBool(Key, "CFI", CaptionFont->Style.Contains(fsItalic));//
ini->WriteBool(Key, "CFU", CaptionFont->Style.Contains(fsUnderline));//
ini->WriteBool(Key, "CFO", CaptionFont->Style.Contains(fsStrikeOut));//
//Чтение параметров шрифта
//ПРи чтении внвчале сбрасываем все параметры шрифта, а потом добавляем
//требуемый в множество в соответствии с прочитанным bool параметром
//Может можно и удалять эелементы из множества,
//но что-то в Help'е описывают только добавление.
CaptionFont->Style.Clear();//Сбросить параметры шрифта
CaptionFont->Name = ini->ReadString(Key, "CFN", CaptionFont->Name);//
CaptionFont->Size = ini->ReadInteger(Key, "CFS", 12);//
CaptionFont->Color = (TColor) ini->ReadInteger(Key, "CFC", clBlack);//
CaptionFont->Charset = (unsigned char)ini->ReadInteger(Key, "CFE", 204);//
if (ini->ReadBool(Key, "CFB", false))//
CaptionFont->Style = CaptionFont->Style << fsBold;//
if (ini->ReadBool(Key, "CFI", false))//
CaptionFont->Style = CaptionFont->Style << fsItalic;//
if (ini->ReadBool(Key, "CFU", false))//
CaptionFont->Style = CaptionFont->Style << fsUnderline;//
if (ini->ReadBool(Key, "CFO", false))//
CaptionFont->Style = CaptionFont->Style << fsStrikeOut;//
//P.S.: по идее должно работать и просто CaptionFont->Style << fsItalic;
//вместо CaptionFont->Style = CaptionFont->Style << fsItalic; |
|
Как записать и считать переменную из ini
C++ |
1
2
3
4
5
6
7
8
9
| void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString info = "ABCDEFGH";
TIniFile *ini = new TIniFile("C:\\Program Files\\Borland\\CBuilder6\\Projects\\pr_ini\\file.ini");
ini->WriteString( "section", "name", info);
AnsiString info2 = ini->ReadString( "section", "name", "No Value");
ShowMessage(info2); // получаем ABCDEFGH
delete ini; ini = NULL;
} |
|
Как считать секцию из файла ini
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #include <Inifiles.hpp>
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TIniFile* pif = new TIniFile("WIN.INI");
pif->ReadSections(ListBox1->Items);
pif->ReadSection("Ports",ListBox2->Items);
pif->ReadSectionValues("Ports",ListBox3->Items);
delete pif;
}
MyIni->WriteInteger("NumOfItems","ItemsCount",ListBox1->Items->Count);
for (int j=0,cnt=ListBox1->Items->Count;j<cnt;j++) {
MyIni->WriteString("Listbox","Item"+IntToStr(j),ListBox1->Items->Strings[j]);
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{
int j,cnt;
cnt=StrToInt(MyIni->ReadString("NumOfItems","ItemsCount","0"));
for (j=0;j<cnt;j++)
ListBox1->Items->Add(MyIni->ReadString("ListBox","Item"+IntToStr(j),"N/A"));
} |
|
Количество секций и ключей
C++ |
1
2
3
4
5
6
7
8
9
| TIniFile *iniFile = TIniFile(iniFileName);
TStringList *iniKeys = new TStringList;
TStringList *iniSections = new TStringList;
iniFile->ReadSections(iniSections);
ShowMessage(IntToStr(iniSections->Count)); // Количество секций
iniFile->ReadSection("General", iniKeys);
ShowMessage(IntToStr(iniKeys->Count)); // Количество ключей |
|
Количество ключей в секции
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| TIniFile *iniFile = TIniFile(iniFileName);
TStringList *iniKeys = new TStringList;
TStringList *iniSections = new TStringList;
iniFile->ReadSections(iniSections);
ShowMessage(IntToStr(iniSections->Count)); // Количество секций
iniFile->ReadSection(SectionName, iniKeys);
ShowMessage(IntToStr(iniKeys->Count)); // Количество ключей
unsigned long int CIni::ulSectionSize(const char *pszSection) {
unsigned long int ulRes = 0;
const unsigned int uiBuffLen = LINES_PER_SECTION * LINE_LEN;
char szBuff[uiBuffLen];
memset(szBuff, 0, uiBuffLen);
GetPrivateProfileSection(pszSection, szBuff, uiBuffLen, m_sFilePath.c_str());
//"Key1=xxx1"0"Key2=xxx2"0"Key2=xxx2"
//если секция пустая
if (szBuff[0] == '\0') {
return 0;
}
//секция не пустая
for (int i = 0; i < uiBuffLen; i ++) {
if (szBuff[i] == '\0') {
ulRes ++;
//конец данных '\0\0'
if (szBuff[i + 1] == '\0') {
break;
}
}
}
return ulRes;
} |
|
Дополнительно
Файл конфигурации
Working with INI Configuration Files Cross-Platform (Win32/Un*x, MBCS/Unicode)
IniFilesTMemIniFile (C++)
Using TIniFile and TMemIniFile
Using TRegistryIniFile
THashedStringList
iniFile
DLL как контейнер языковых файлов
Клавиша руки обжигает!
Скрыть строку с memo
INI Файл и Стиль Шрифта - как сохранить в INI стиль шрифта.
Клавиша руки обжигает!
Максимальная длина строки TMemo/TRichEdit/TIniFile
StringList - Связь с INI-файлами
Парсер файлов ini
Работа с файлами INI
SimpleIni
How to use an ini file in C++Builder
Исходники
Запись и считывание очень длинной строки
Проект с примером реализации:
Калькулятор Buidler
Не по теме:
Всех источников не помню. По поводу авторских прав, ошибок, дополнений и улучшений - пишите в личку.