Как читать UI файл с кодировкой UTF8?

У меня есть файл INI в формате UTF-8.

Я использую Delphi 2010 для чтения INI файла и заполнения TStringGrid значениями в INI файле.

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 символ юникода.

Как это разрешить?

Ответ 1

Класс TIniFile является оболочкой Windows API для файлов INI. Это поддерживает Unicode INI файлы, но только если эти файлы закодированы как UTF-16. Майкл Каплан имеет более подробную информацию: Unicode INI функция; Unicode INI файл?

Итак, вам не повезло с TIniFile. Вместо этого вы можете использовать TMemIniFile, который позволяет указать кодировку в своем конструкторе. Класс TMemIniFile является встроенной поддержкой файлов INI в Delphi. Между двумя классами существуют разные плюсы и минусы. В вашей ситуации только TMemIniFile может удовлетворить ваши потребности, поэтому он выглядит как его профи, перевешивает его недостатки.

Ответ 2

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;

Ответ 3

В приложении, где я использовал 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 файл сохраняться с правильной кодировкой, это означает, что если INI файл, закодированный в ANSI уже существует, он перезаписывается и становится UTF-8, поэтому транзакция между ANSI и UTF-8 гладкая и совсем не болезненная.