12

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?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
user1527613
  • 1,100
  • 4
  • 12
  • 29
  • 4
    You can use [`TMemIniFile`](http://docwiki.embarcadero.com/Libraries/XE2/en/System.IniFiles.TMemIniFile) which has in one of its [`constructor`](http://docwiki.embarcadero.com/Libraries/XE2/en/System.IniFiles.TMemIniFile.Create) overloads `Encoding` parameter. Into this parameter you'll just pass `TEncoding.UTF8`. The rest of your code will remain same. Btw. try to learn how to use `try..finally` block to make sure the `AppIni` object will be released after its use. – TLama May 03 '13 at 17:58
  • Here's [`the code`](http://pastebin.com/nX0PAZNw) I was talking about. – TLama May 03 '13 at 19:53

3 Answers3

17

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.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David H says "only if those files are encoded as UTF-16", yet TLama says "you'll just pass TEncoding.UTF8". So which is it? – user1527613 May 03 '13 at 19:06
  • I have tried using TMemIniFile instead of TIniFile, yet when I try to save a new INI, the file never gets created. Here's the create: AppUnicodeINI := MemIniFile.Create(ExtractFilePath(Application.ExeName) + 'test.ini',TEncoding.Unicode); And here is the write method: AppUnicodeINI.WriteString('Column1','Row'+IntToStr(ItemNo),StringGrid1.Cells[0,ctr]); Then finally AppUnicodeINI.Free; Yet no file is created. – user1527613 May 03 '13 at 19:10
  • Both of us are correct. TLama says pass `TEncoding.UTF8` to `TMemIniFile`. And I say that too. That's my second paragraph. And I also say in paragraph 1 that you cannot use `TIniFile` because it only supports UTF-16. Note that `TMemIniFile` and `TIniFile` are not the same class. I think that's where you are confused. – David Heffernan May 03 '13 at 19:12
  • I understand that I cannot use TIniFile. Where I am confused is that TLama made reference to UTF8, whereas you said UTF16. Moreover, using either with TMemIniFile still produces no output. UTF8 is not UTF16. – user1527613 May 03 '13 at 19:13
  • Eureka! Looks like someone forgot to tell me to insert UpdateFile into my code, else nothing will get written to disk! :-( – user1527613 May 03 '13 at 19:19
  • 4
    @user1527613, nobody forgot to tell you how to flush `TMemIniFile` to disk just because you've started your question with *"I am using Delphi 2010 to read the INI file"* as well as you were obviously only reading values from the INI file in your sample code. The question you've asked is answered here. – TLama May 03 '13 at 19:41
  • 1
    Well, as @TLama says, you said you were reading from the file. You did not ask how to write. Anyway, the documentation says this: http://docwiki.embarcadero.com/Libraries/en/System.IniFiles.TMemIniFile – David Heffernan May 03 '13 at 19:44
  • @user1527613 Are you looking for more than this answer? – David Heffernan May 04 '13 at 19:31
  • Which are the drawbacks for TMemIniFile? – Gabriel Jan 10 '18 at 18:06
  • @DavidHeffernan Could you elaborate on the cons of reading INI files with TMemIniFile? For me nothing comes to mind... – Alois Heimer Mar 02 '18 at 10:43
  • @AloisHeimer I don't know of any. – David Heffernan Mar 02 '18 at 10:57
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;
Edijs Kolesnikovičs
  • 1,627
  • 3
  • 18
  • 34
0

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.

UnDiUdin
  • 14,924
  • 39
  • 151
  • 249