全力提升 TIniFile 的读取速度

全力提升 TIniFile 的读取速度

软件保存设置一般是写入本地目录,或者写入注册表。前者使用的文件格式很多,多为 IniFile (现在 XML 也开始流行)。后者是将信息储存在注册表中,并通过的 TRegistry 或者标准的 WindowsAPI 来读写。后者有个很大的好处:不需要对多用户系统进行考虑,Windows 可以很好的控制多用户的切换。但缺点是往往卸载软件不将注册表中生成的记录信息删除干净。而制作绿色软件的作者多采用 IniFile 方式保存信息。

IniFile 本来也没有什么好多说的,有现成的工具 TIniFile,也有标准的 WindowsAPI。不过偶然发现当读取 IniFile 的一整个记录较多的 Section 时,性能很差。根据我的测试,当一个 Section 有超过 61 条记录的时候,比直接用 TStringList 打开一个文件,并分析出 Section 的记录要慢。问题出在哪里呢?我估计可能是大量的时间被耗费在打开文件和记录定位上了。

设想一下:如果我们将带有 600 条记录的语言文件保存在 IniFile 中。用上述2种方法在速度上的差异甚大。我在 GOSURF 浏览器中做了比较。使用标准的 TIniFile 读取需要 2.78 秒,而使用了利用 TStringList 之后,时间缩短为 0.102秒。这 2 秒的时间对于一个软件的启动来说,算是相当长的了。

随后我对 TIniFile 的各种操作进行了测试,发现 ReadSectionValues 是有性能明显性能瓶颈的。于是动手改造了一番:

procedure TTntIniFile.ReadSectionValuesEx(const Section: WideString; Strings: TTntStrings);

var

BeginIdx, EndIdx: Integer;

KeyValues: TTntStringList;

I: Integer;

S: WideString;

begin

KeyValues := TTntStringList.Create;

try

    KeyValues.LoadFromFile(FileName);

    BeginIdx := 0;

    while (BeginIdx <= KeyValues.Count - 1) do

    begin

      if Trim(KeyValues[BeginIdx]) = '[' + Section + ']' then Break;

      Inc(BeginIdx);

    end;

    EndIdx := BeginIdx + 1;

    while (EndIdx <= KeyValues.Count - 1) do

    begin

      S := Trim(KeyValues[EndIdx]);

      if (S <> '') and (S[1] = '[') and (S[Length(S)] = ']') then Break;

      Inc(EndIdx);

    end;

    Strings.BeginUpdate;

    try

      for I := BeginIdx + 1 to EndIdx - 1 do

        if KeyValues[I] <> '' then

           Strings.Add(KeyValues[I]);

    finally

      Strings.EndUpdate;

    end;

finally

    KeyValues.Free;

end;

end;

注1:如果没有安装过 TntControls 的朋友,只要将 WideString 改成 string,将 TTnt 开头的对象都改成 T 即可。

注2:第二个参数 Strings: TTntStrings 是抽象类。你可以使用 TStringList,不过我推荐使用 THashedStringList。当记录数量很大的时候搜索某条记录 (如:Strings.Values['LastRecode']) 可能也相当耗时。不使用 THashedStringList 那 ReadSectionValuesEx 的优势无法完全体现。

另外,DELPHI 中还提供了一种 TMemIniFile。原理是一次性将整个 IniFile 文件读入内存,以提高响应速度。但灵活性和稳定性不如使用 TIniFile + ReadSectionValuesEx 来的好。当然将常用的调用单条记录制作成函数会更加灵活方便。如 IniReadString(const FileName, Section, Ident, Default: WideString): WideString; 这些函数就请你自己扩充了 :D

最后说一下 XML。它的性质和 IniFile 差不多。不过他的结构更多样化,你可以轻松把一个 Object 保存在文件中,阅读也比较直观。我把 IniFile 文件看成简单的一层目录结构,那 XML 是多层目录结构。当然分析一个 XML 也相对会稍慢一些。待有时间,再向大家推荐好用高效的 TXMLParser。

Read: 1238