user1527613
user1527613

Reputation: 1120

How do I read a UTF8 encoded INI file?

I have an INI file in UTF-8 format.

I am using Delphi 2010 to read the INI file and populate a TStringGrid with the values in the INI file.

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;

The problem is that the unicode characters are appearing in the TStringGrid displaying 2 characters, rather than the 1 unicode character.

How do I resolve this?

Upvotes: 12

Views: 12536

Answers (4)

NKnabe
NKnabe

Reputation: 15

Citing the answer above by David Heffernan, there is no need to use TMemIniFile provided the INI-files are UTF-16. A draw back with TMemIniFile is, that TMemIniFile.UpdateFile deletes any header of the INI-file unless subordinate a square bracketed [Header].

In short, you will need no more than adding this file conversion to your code, and your standard INI-file read/write will support Unicode (no need for TEncoding.UTF8):

procedure TForm1.FormCreate(Sender: TObject);
var
  slINItemp: TStringList;
begin
  if FileExists('MyINIfile.ini') then
    Try
      slINItemp := TStringList.Create;
      slINItemp.LoadFromFile('MyINIfile.ini'));
      slINItemp.SaveToFile('MyINIfile.ini',TEncoding.Unicode);
      slINItemp.Free;
    except
    end;
  ...
end;

Tested on Windows 7(32-bit) and Windows 10(64-bit) created with Delphi 10.4.2.

Otherwise, you may just convert existing INI-files with e.g. Notepad.exe, and your existing (Unicode enabled) application may suddenly benefit from Unicode supporting INI-files.

Upvotes: 0

UnDiUdin
UnDiUdin

Reputation: 15374

In an application were I was using TIniFile i had the need to start storing Unicode chars.

To do this i simply changed the variable type from TIniFile to TMemIniFile and in the constructor, after the filename i added the second parameter TEncoding.UTF8. Then before freeing the object i called UpdateFile. If Ini File is opened for reading, call to UpdateFile is not needed.

// 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

The good news is that UpdateFile causes the ini file to be saved with the proper encoding, this means that if a ini file encoded in ANSI already exists it is overwriten so it becomes UTF-8, so the transaction between ANSI and UTF-8 is smooth and not painful at all.

Upvotes: 0

Edijs Kolesnikovičs
Edijs Kolesnikovičs

Reputation: 1695

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;

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 612784

The TIniFile class is a wrapper of the Windows API for INI files. This does support Unicode INI files, but only if those files are encoded as UTF-16. Michael Kaplan has more details here: Unicode INI function; Unicode INI file?

So, you are out of luck with TIniFile. Instead you could use TMemIniFile which allows you to specify an encoding in its constructor. The TMemIniFile class is a native Delphi implementation of INI file support. There are various pros and cons between the two classes. In your situation, only TMemIniFile can serve your needs, so it's looking like its pros are going to outweigh its cons.

Upvotes: 17

Related Questions