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 一样 | 不需要写存盘的代码,因为每次操作都和文件内容同步 | |