C++ Builder 支持的 ini 文件的结构
简单的 ini 文件的结构
[Section1]
Indent1=Value1
Indent2=Value2
[Section2]
Indent3=Value3
Indent4=Value4 |
含有 SubSections 的 ini 文件结构
注1: 不要混用 “.”, “/” 和 “\” 做分割符,程序里面的写法要和文件里面的一致,
例如 "Section1.SubSection1" 只是对应 [Section1.SubSection1] 而不对应 [Section1/SubSection1];
注2: 在 C/C++ 里面 “\” 是需要有转义符的,在程序里面不是 "Section1\SubSection1" 而是 "Section1\\SubSection1"。
用 . 分割 SubSections 的 ini 文件 |
用 / 分割 SubSections 的 ini 文件 |
用 \ 分割 SubSections 的 ini 文件 |
[Section1]
Indent1=Value1
Indent2=Value2
[Section2]
Indent3=Value3
Indent4=Value4
[Section1.SubSection1]
Indent5=Value5
Indent6=Value6
Indent7=Value7
[Section1.SubSection2]
Indent8=Value8
Indent9=Value9
[Section1.SubSection2.SubSubSec1]
IndentA=ValueA
IndentB=ValueB
IndentC=ValueC
[Section1.SubSection2.SubSubSec2]
IndentD=ValueD
IndentE=ValueE
IndentF=ValueF |
[Section1]
Indent1=Value1
Indent2=Value2
[Section2]
Indent3=Value3
Indent4=Value4
[Section1/SubSection1]
Indent5=Value5
Indent6=Value6
Indent7=Value7
[Section1/SubSection2]
Indent8=Value8
Indent9=Value9
[Section1/SubSection2/SubSubSec1]
IndentA=ValueA
IndentB=ValueB
IndentC=ValueC
[Section1/SubSection2/SubSubSec2]
IndentD=ValueD
IndentE=ValueE
IndentF=ValueF |
[Section1]
Indent1=Value1
Indent2=Value2
[Section2]
Indent3=Value3
Indent4=Value4
[Section1\SubSection1]
Indent5=Value5
Indent6=Value6
Indent7=Value7
[Section1\SubSection2]
Indent8=Value8
Indent9=Value9
[Section1\SubSection2\SubSubSec1]
IndentA=ValueA
IndentB=ValueB
IndentC=ValueC
[Section1\SubSection2\SubSubSec2]
IndentD=ValueD
IndentE=ValueE
IndentF=ValueF |
C++ Builder 处理 ini 文件的头文件
新版本 C++ Builder 的 ini 文件处理头文件为 #include <System.IniFiles.hpp>
老版本 C++ Builder 的 ini 文件处理头文件为 #include <IniFiles.hpp> 新版本目前保留了这个头文件 (说不定再更新哪个版本就没有了)。
TMemIniFile 和 TIniFile 都在这个头文件里面。
使用 TMemIniFile 处理 ini 文件
指定 ini 文件的文件名和字符编码
TMemIniFile *ini = new TMemIniFile(L"d:\\abc.ini"); // 系统默认的文本文件字符编码 (ANSI) |
TMemIniFile *ini = new TMemIniFile(L"d:\\abc.ini", TEncoding::UTF8); // UTF-8 编码 |
TMemIniFile *ini = new TMemIniFile(L"d:\\abc.ini", TEncoding::Unicode); // UNICODE UTF-16 |
TEncoding *encBig5 = new TMBCSEncoding(950);
TMemIniFile *ini = new TMemIniFile(L"d:\\abc.ini", encBig5); // BIG5 编码 |
TEncoding *encGBK = new TMBCSEncoding(936);
TMemIniFile *ini = new TMemIniFile(L"d:\\abc.ini", encGBK); // GBK 编码 |
虽然 ini 文件可以中途改文字编码,例如:
ini->Encoding = TEncoding::UTF8;
但是最好不要这样做,可能会导致乱码。
如果需要给文件转编码,可以使用 TStringList,方法请点击这里。
指定的字符编码和 ini 文件本身的编码不同,打开文件会报错,例如文件是 ANSI 编码的,按照 UTF-8 读取,会报错,抛出异常。
重命名文件:Rename(新文件名, bool Reload) 的第二个参数 Reload:
ini->Rename(L"d:\\b.ini",false); // Reload=false: 重命名为 b.ini,如果有这个文件,会覆盖,没有会新创建 |
ini->Rename(L"d:\\a.ini",true); // Reload=true: 放弃原来的文件,重新加载 a.ini 文件,如果没有会新创建 |
AutoSave 属性和 UpdateFile() 方法
默认情况,TMemIniFile 不会自动保存 ini 文件。
• AutoSave 属性设为 true 的情况,会在销毁 TMemIniFile 对象时存盘;默认值为 false,在销毁 TMemIniFile 对象的时候不存盘。
• 调用 UpdateFile() 方法,会把修改的内容存盘。一般情况,在修改 ini 之后,调用 UpdateFile() 存盘。
#include <memory>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
std::auto_ptr<TMemIniFile>ini(new TMemIniFile(L"d:\\abc.ini",TEncoding::Unicode));
ini->WriteInteger(L"OPTIONS",L"Volume",100);
ini->WriteInteger(L"OPTIONS",L"Rate",0);
ini->UpdateFile();
} |
读写数据
下面表格是读取各种数据类型数据的方法,读取 [Section] 里面的值 Ident,如果值不存在,返回默认值 Default
读取数据的方法 |
函数原型 |
说明 |
ReadString |
UnicodeString __fastcall ReadString(const UnicodeString Section, const UnicodeString Ident, const UnicodeString Default); |
读取字符串值 |
ReadInteger |
int __fastcall ReadInteger(const UnicodeString Section, const UnicodeString Ident, int Default); |
读取整数值 |
ReadBool |
bool __fastcall ReadBool(const UnicodeString Section, const UnicodeString Ident, bool Default); |
读取布尔值 |
ReadFloat |
double __fastcall ReadFloat(const UnicodeString Section, const UnicodeString Name, double Default); |
读取浮点数值 |
ReadDate |
TDateTime __fastcall ReadDate(const UnicodeString Section, const UnicodeString Name, TDateTime Default); |
读取日期 |
ReadTime |
TDateTime __fastcall ReadTime(const UnicodeString Section, const UnicodeString Name, TDateTime Default); |
读取时间 |
ReadDateTime |
TDateTime __fastcall ReadDateTime(const UnicodeString Section, const UnicodeString Name, TDateTime Default); |
读取日期和时间 |
ReadBinaryStream |
int __fastcall ReadBinaryStream(const UnicodeString Section, const UnicodeString Name, TStream* Value); |
读取二进制数据 |
下面表格是写入数据的方法,把 Indent 的数据写入 [Section],值为 Value,如果 [Section] 或 Indent 不存在会新增,如果存在会改写。
写入数据的方法 |
函数原型 |
说明 |
WriteString |
void __fastcall WriteString(const UnicodeString Section, const UnicodeString Ident, const UnicodeString Value); |
写入字符串值 |
WriteInteger |
void __fastcall WriteInteger(const UnicodeString Section, const UnicodeString Ident, int Value); |
写入整数值 |
WriteBool |
void __fastcall WriteBool(const UnicodeString Section, const UnicodeString Ident, bool Value); |
写入布尔值 |
WriteFloat |
void __fastcall WriteFloat(const UnicodeString Section, const UnicodeString Name, double Value); |
写入浮点数 |
WriteDate |
void __fastcall WriteDate(const UnicodeString Section, const UnicodeString Name, TDateTime Value); |
写入日期 |
WriteTime |
void __fastcall WriteTime(const UnicodeString Section, const UnicodeString Name, TDateTime Value); |
写入时间 |
WriteDateTime |
void __fastcall WriteDateTime(const UnicodeString Section, const UnicodeString Name, TDateTime Value); |
写入日期和时间 |
WriteBinaryStream |
void __fastcall WriteBinaryStream(const UnicodeString Section, const UnicodeString Name, TStream* Value); |
写入二进制数据 |
判断是否存在 [Section] 里面的 Indent 值
判断数据是否存在 |
函数原型 |
说明 |
ValueExists |
bool __fastcall ValueExists(const UnicodeString Section, const UnicodeString Ident); |
判断是否存在 [Section] 里面的 Indent |
删除数据
删除数据 |
函数原型 |
说明 |
DeleteKey |
void __fastcall DeleteKey(const UnicodeString Section, const UnicodeString Ident); |
删除 [Section] 里面的 Indent |
读取 Sections
读取 Sections |
函数原型 |
说明 (示例为处理 “含有 SubSections 的 ini 文件结构” 的结构示例表格里面的 “用 . 分割 SubSections 的 ini 文件”) |
ReadSection |
void __fastcall ReadSection(const UnicodeString Section, TStrings* Strings); |
获取 [Section] 里面所有的 Indent,例如:读取 "Section1.SubSection2" 得到的是:
Indent8
Indent9 |
ReadSections |
void __fastcall ReadSections(TStrings* Strings); |
获取所有 Section 的名字,例如读取结构示例能够得到:
Section1
Section2
Section1.SubSection1
Section1.SubSection2
Section1.SubSection2.SubSubSec1
Section1.SubSection2.SubSubSec2 |
ReadSections |
void __fastcall ReadSections(const UnicodeString Section, TStrings* Strings); |
获取所有名字以参数 Section 指定的字符串开头的 Section 的名字。如果 Section 给定的是空字符串,执行结果和前一个只有一个参数的 ReadSections 函数相同。 |
ReadSubSections |
void __fastcall ReadSubSections(const UnicodeString Section, TStrings* Strings, bool Recurse = false); |
获取所有 SubSections 的名字,
例如获取 "Section1.SubSection2"
得到的是
SubSubSec1 和
SubSubSec2,
例如获取 "Section1"
得到的是 SubSection1,
SubSection2,
SubSection2.SubSubSec1,
SubSection2.SubSubSec2 |
ReadSectionValues |
void __fastcall ReadSectionValues(const UnicodeString Section, TStrings* Strings); |
获取 [Section] 里面所有的值,
例如获取 "Section1"
得到的是
Indent1=Value1
Indent2=Value2 |
SectionExists |
bool __fastcall SectionExists(const UnicodeString Section); |
判断 [Section] 是否存在。 |
删除 Sections
删除 Sections |
函数原型 |
说明 |
EraseSection |
void __fastcall EraseSection(const UnicodeString Section); |
删除整个 [Section] |
整体文件的数据操作
整体文件数据操作 |
函数原型 |
说明 |
Clear |
void __fastcall Clear(void); |
清除整个 ini 文件的内容 |
GetStrings |
void __fastcall GetStrings(TStrings* const List); |
获取整个 ini 文件的内容到 List |
SetStrings |
void __fastcall SetStrings(TStrings* const List); |
整个 ini 的文件内容更换为 List 的内容 |
TMemIniFile 的属性成员
属性 |
类型 |
说明 |
AutoSave |
bool |
是否在销毁 TMemIniFile 对象时保存文件,默认为 false,不保存。 |
Modified |
bool |
ini 文件数据是否被修改过 |
CaseSensitive |
bool |
关键字 (Section 和 Ident) 是否区分大小写 |
FileName |
UnicodeString |
文件名 (只读),在创建对象时指定文件名,或者用 Rename() 改名。 |
Encoding |
TEncoding * |
文件的字符编码 |
使用 TIniFile 处理 ini 文件
在使用上,TIniFile 和 TMemIniFile 只有两点不同的地方:
• 不需要用 UpdateFile() 存盘,也不需要指定 AutoSave 属性,TIniFile 始终和文件同步,每次读写数据都是在读写文件;
• 不能指定字符编码,始终使用默认的编码 (ANSI) 创建新的文件,读取的时候,支持 ANSI 和 UNICODE (UTF-16)
使用 TIniFile 的注意事项:
• 不推荐使用 TIniFile,推荐使用 TMemIniFile 处理 ini 文件;
• TIniFile 用的是过时的 API 函数 GetPrivateProfileString 和
WritePrivateProfileString 处理的 ini 文件,在 Windows 8 和 Windows 10 操作系统里面,出现访问系统盘里面的文件的兼容性问题。MSDN 里面描述的这两个 API 函数是为了兼容 16 位 Windows 应用保留的函数。
• TIniFile 只能新建 ANSI 编码的文件,可以读写 ANSI 和 UNICODE (UTF-16) 编码的文件,无法读写 UTF-8 或其他编码的文件。
TMemIniFile 和 TIniFile,应该使用哪一个,他们有什么区别?
用下面的表格比较 TMemIniFile 和 TIniFile,很明显 TMemIniFile 获胜,应该使用 TMemIniFile。
项目 |
TMemIniFile |
TIniFile |
读写方法 |
使用 TFileStream 读写文件,支持用 TEncoding 指定字符编码 |
使用的是过时的 GetPrivateProfileString 和
WritePrivateProfileString,这两个为了兼容 16 位 Windows 应用保留下来的 API 函数 |
兼容性 |
使用的是通用的文件处理方法,没有兼容性问题 |
在 Windows 8 之后的系统里面,读写系统盘里面的文件可能会出现无法读写文件内容的兼容性问题 |
文字编码 |
使用 TEncoding,支持所有操作系统支持的文字编码,包括 UNICODE (UTF-16) 和 UTF-8 |
新创建的 ini 文件只能是 ANSI 编码的,可以读取 ANSI 和 UNICODE (UTF-16) 的文件,无法读取 UTF-8 或其他编码的文件 |
效率 |
所有的内容都在内存里面处理,速度快,效率高 |
所有的处理都和文件同步,每次操作都读写文件,速度慢,效率低 |
使用区别 |
修改之后,需要用 UpdateFile() 存盘,也可以设 AutoSave 属性为 true,在修改之后自动保存文件。其他和 TIniFile 一样 |
不需要写存盘的代码,因为每次操作都和文件内容同步 |
|