如何读取一个UTF8编码的INI文件?

12

我有一个以UTF-8格式编码的INI文件。

我正在使用Delphi 2010读取该INI文件,并将其中的值填充到TStringGrid中。

var
  ctr : Integer;
  AppIni : TIniFile;
begin
  AppIni := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'test.ini');
    for ctr := 1 to StringGrid1.RowCount do begin
        StringGrid1.Cells[0,ctr] := AppIni.ReadString('Column1','Row'+IntToStr(ctr),'');
        StringGrid1.Cells[1,ctr] := AppIni.ReadString('Column2','Row'+IntToStr(ctr),'');
    end;
  AppIni.Free;

问题在于Unicode字符在TStringGrid中显示为2个字符,而不是1个Unicode字符。

我该如何解决这个问题?


4
你可以使用 TMemIniFile,其中一个 constructor 重载了 Encoding 参数。你只需要将 TEncoding.UTF8 传递给这个参数即可,其余的代码保持不变。顺便说一句,试着学习如何使用 try..finally 块来确保在使用后释放 AppIni 对象。 - TLama
这是我所说的代码 - TLama
3个回答

17
TIniFile类是Windows API针对INI文件的包装器。虽然它支持Unicode INI文件,但前提是这些文件以UTF-16编码。Michael Kaplan在此处提供了更多详细信息:Unicode INI function; Unicode INI file? 所以,如果你想使用Unicode INI文件,TIniFile将不起作用。相反,您可以使用TMemIniFile,它允许您在其构造函数中指定编码方式specify an encoding in its constructorTMemIniFile类是INI文件支持的本机Delphi实现。这两个类之间有各种优缺点。在您的情况下,只有TMemIniFile能够满足您的需求,因此看起来其优点将超过其缺点。

David H说:“只有这些文件被编码为UTF-16时”,然而TLama说:“你只需传递TEncoding.UTF8”。那么到底是哪个呢? - user1527613
我已经尝试使用 TMemIniFile 替换 TIniFile,但是当我尝试保存新的 INI 文件时,文件从未被创建。这是创建代码:AppUnicodeINI := MemIniFile.Create(ExtractFilePath(Application.ExeName) + 'test.ini',TEncoding.Unicode);这是写入方法:AppUnicodeINI.WriteString('Column1','Row'+IntToStr(ItemNo),StringGrid1.Cells[0,ctr]);最后执行 AppUnicodeINI.Free;但是没有文件被创建。 - user1527613
我们两个都是正确的。TLama说要将TEncoding.UTF8传递给TMemIniFile。我也是这么说的。这是我的第二段。我在第一段中还说,你不能使用TIniFile,因为它只支持UTF-16。请注意,TMemIniFileTIniFile不是同一个类。我想这就是你困惑的地方。 - David Heffernan
4
@user1527613,没有人忘记告诉您如何将TMemIniFile刷新到磁盘,只是因为您在问题中明确提到了“我正在使用Delphi 2010读取INI文件”,而且在示例代码中显然只读取了INI文件中的值。您提出的问题在这里得到了回答。 - TLama
1
好的,正如@TLama所说,你说你正在从文件中读取。你没有问如何写入。无论如何,文档说:http://docwiki.embarcadero.com/Libraries/en/System.IniFiles.TMemIniFile - David Heffernan
显示剩余6条评论

3
Uses IniFiles;

const
  SZ_APP_NAME = 'demo_test';

Procedure TForm1.GetSettings;
var
  _MemIniU: TMemIniFile;
  _SettingsPath: string;
begin
  try
    _SettingsPath := GetHomePath + PathDelim + SZ_APP_NAME + PathDelim;
    if ForceDirectories(_SettingsPath) then
    begin
      _MemIniU := TMemIniFile.Create(ChangeFileExt(_SettingsPath,
        'Settings.ini'), TEncoding.UTF8);
      try
        if _MemIniU.ReadInteger(SZ_APP_NAME, 'WindowLeft', -1) = -1 then
          Form1.Position := poScreenCenter
        else
        begin
          Form1.Left := _MemIniU.ReadInteger(SZ_APP_NAME, 'WindowLeft', 10);
          Form1.Top := _MemIniU.ReadInteger(SZ_APP_NAME, 'WindowTop', 10);
          Form1.Width := _MemIniU.ReadInteger(SZ_APP_NAME, 'WindowWidth', 594);
          Form1.Height := _MemIniU.ReadInteger(SZ_APP_NAME,
            'WindowHeight', 342);
        end;
          Edit1.Text := _MemIniU.ReadString(SZ_APP_NAME, 'UnicodeText', 'ąčę');
      finally
        _MemIniU.Free;
      end;
    end;
  except
    on E: Exception do
      MessageDlg(PWideChar(E.Message), TMsgDlgType.mtError,
        [TMsgDlgBtn.mbOK], 0);
  end;
end;

Procedure TForm1.SaveSettings;
var
  _MemIniU: TMemIniFile;
  _SettingsPath: string;
begin
  try
    _SettingsPath := GetHomePath + PathDelim + SZ_APP_NAME + PathDelim;
    _MemIniU := TMemIniFile.Create(ChangeFileExt(_SettingsPath, 'Settings.ini'),
      TEncoding.UTF8);
    try
      if Form1.WindowState <> TWindowState.wsMaximized then
      begin
        _MemIniU.WriteInteger(SZ_APP_NAME, 'WindowLeft', Form1.Left);
        _MemIniU.WriteInteger(SZ_APP_NAME, 'WindowTop', Form1.Top);
        _MemIniU.WriteInteger(SZ_APP_NAME, 'WindowWidth', Form1.Width);
        _MemIniU.WriteInteger(SZ_APP_NAME, 'WindowHeight', Form1.Height);
        _MemIniU.WriteString(SZ_APP_NAME, 'UnicodeText', Edit1.Text);
      end;
      _MemIniU.UpdateFile;
    finally
      _MemIniU.Free;
    end;
  except
    on E: Exception do
      MessageDlg(PWideChar(E.Message), TMsgDlgType.mtError,
        [TMsgDlgBtn.mbOK], 0);
  end;
end;

0
在一个应用程序中,我使用了TIniFile,我需要开始存储Unicode字符。
为了做到这一点,我只需将变量类型从TIniFile更改为TMemIniFile,并在构造函数中,在文件名后添加第二个参数TEncoding.UTF8。然后在释放对象之前,我调用了UpdateFile。如果Ini文件是以读取方式打开的,则不需要调用UpdateFile
// ANSI version
var myIniFile: TIniFile;
begin
  myIniFIle := TIniFile.Create('c:\Temp\MyFile.ini');
  myIniFile.WriteString(par1,par2,par3);
  // [...]
  myIniFile.Free;
end


// Unicode version
//1) "Mem" added here
var myIniFile: TMemIniFile; 
begin
  // 2) Enconding added
  myIniFIle := TIniFile.Create('c:\Temp\MyFile.ini', TEncoding.UTF8); 
  myIniFile.WriteString(par1,par2,par3);
  // [...]
  // 3) call to UpdateFile to save to disc the changes
  myIniFile.UpdateFile; 
  myIniFile.Free;
end

好消息是,UpdateFile会导致ini文件以正确的编码保存,这意味着如果已经存在一个使用ANSI编码的ini文件,它将被覆盖为UTF-8,因此在ANSIUTF-8之间的转换非常顺利,一点也不痛苦。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接