View Issue Details

IDProjectCategoryView StatusLast Update
0032452FPCFCLpublic2017-12-30 10:45
ReporterOndrej PokornyAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version3.1.1Product Build 
Target Version3.2.0Fixed in Version3.1.1 
Summary0032452: [Feature, patch] Add Encoding-aware constructor for TCustomIniFile
DescriptionThere's no chance to use encoding in INI files. As a result, Lazarus (that uses UTF-8 string type) cannot read ANSI INI files on Windows.

A solution is to add a constructor overload that accepts a TEncoding.
Additional InformationPatch attached.
TagsNo tags attached.
Fixed in Revision37852
FPCOldBugId
FPCTarget
Attached Files
  • inifiles.pp-encoding-01.patch (6,207 bytes)
    Index: packages/fcl-base/src/inifiles.pp
    ===================================================================
    --- packages/fcl-base/src/inifiles.pp	(revision 37292)
    +++ packages/fcl-base/src/inifiles.pp	(working copy)
    @@ -153,8 +153,10 @@
       Private
         FBoolFalseStrings: TStringArray;
         FBoolTrueStrings: TStringArray;
    +    FEncoding: TEncoding;
         FFileName: string;
         FOptions: TIniFileOptions;
    +    FOwnsEncoding: Boolean;
         FSectionList: TIniFileSectionList;
         function GetOption(AIndex: TIniFileOption): Boolean;
         procedure SetOption(AIndex: TIniFileOption; AValue: Boolean);
    @@ -161,6 +163,8 @@
         procedure SetOptions(AValue: TIniFileOptions);
       public
         FormatSettings: TFormatSettings;
    +    constructor Create(const AFileName: string; AEncoding: TEncoding; AOptions : TIniFileOptions = []);
    +    constructor Create(const AFileName: string; AEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions : TIniFileOptions = []);
         constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); virtual;
         constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); virtual;
         destructor Destroy; override;
    @@ -192,6 +196,7 @@
         procedure DeleteKey(const Section, Ident: String); virtual; abstract;
         procedure UpdateFile; virtual; abstract;
         function ValueExists(const Section, Ident: string): Boolean; virtual;
    +    property Encoding: TEncoding read FEncoding;
         property FileName: string read FFileName;
         Property Options : TIniFileOptions Read FOptions Write SetOptions;
         property EscapeLineFeeds: boolean index ifoEscapeLineFeeds Read GetOption ;deprecated 'Use options instead';
    @@ -200,6 +205,7 @@
         Property FormatSettingsActive : Boolean index ifoFormatSettingsActive Read GetOption Write SetOption;deprecated  'Use options instead';
         Property BoolTrueStrings : TStringArray Read FBoolTrueStrings Write FBoolTrueStrings;
         Property BoolFalseStrings : TStringArray Read FBoolFalseStrings Write FBoolFalseStrings;
    +    Property OwnsEncoding: Boolean Read FOwnsEncoding;
       end;
     
       { TIniFile }
    @@ -215,6 +221,7 @@
         Procedure MaybeDeleteSection(ASection : TIniFileSection);
         procedure SetCacheUpdates(const AValue: Boolean);
       protected
    +    procedure ReadIniValues;
         procedure MaybeUpdateFile;
         property Dirty : Boolean Read FDirty;
       public
    @@ -599,9 +606,28 @@
         Create(AFileName,[])
     end;
     
    +constructor TCustomIniFile.Create(const AFileName: string;
    +  AEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions: TIniFileOptions);
    +begin
    +  FEncoding := AEncoding;
    +  FOwnsEncoding := AOwnsEncoding;
    +  Create(AFileName, AOptions);
    +end;
    +
    +constructor TCustomIniFile.Create(const AFileName: string;
    +  AEncoding: TEncoding; AOptions: TIniFileOptions);
    +begin
    +  FEncoding := AEncoding;
    +  if FEncoding <> nil then
    +    FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
    +  Create(AFileName, AOptions);
    +end;
    +
     destructor TCustomIniFile.Destroy;
     begin
       FSectionList.Free;
    +  if FOwnsEncoding then
    +    FEncoding.Free;
       inherited Destroy;
     end;
     
    @@ -902,9 +928,7 @@
     { TIniFile }
     
     
    -constructor TIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
    -var
    -  slLines: TStringList;
    +constructor TIniFile.Create(const AFileName: string; AOptions: TIniFileoptions);
     begin
       FBOM := '';
       If Not (self is TMemIniFile) then
    @@ -911,17 +935,7 @@
         StripQuotes:=True;
       inherited Create(AFileName,AOptions);
       FStream := nil;
    -  slLines := TStringList.Create;
    -  try
    -    if FileExists(FFileName) then
    -      begin
    -      // read the ini file values
    -      slLines.LoadFromFile(FFileName);
    -      FillSectionList(slLines);
    -      end
    -  finally
    -    slLines.Free;
    -  end;
    +  ReadIniValues;
     end;
     
     constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean);
    @@ -933,7 +947,7 @@
         Create(AStream,[]);
     end;
     
    -constructor TIniFile.Create(AStream: TStream; AOptions : TIniFileOptions = []);
    +constructor TIniFile.Create(AStream: TStream; AOptions: TIniFileoptions);
     
     var
       slLines: TStringList;
    @@ -952,7 +966,7 @@
       end;
     end;
     
    -destructor TIniFile.destroy;
    +destructor TIniFile.Destroy;
     begin
       If FDirty and FCacheUpdates then
         try
    @@ -1233,7 +1247,7 @@
       ASection.Free;
     end;
     
    -Procedure TIniFile.MaybeDeleteSection(ASection : TIniFileSection);
    +procedure TIniFile.MaybeDeleteSection(ASection: TIniFileSection);
     
     begin
       If Asection.Empty then
    @@ -1306,7 +1320,10 @@
           If D <> '' Then
             if not ForceDirectories(D) then
               Raise EInoutError.CreateFmt(SErrCouldNotCreatePath,[D]);
    -      slLines.SaveToFile(FFileName);
    +      if FEncoding=nil then
    +        slLines.SaveToFile(FFileName)
    +      else
    +        slLines.SaveToFile(FFileName, FEncoding);
           end
         else if FStream <> nil then
           begin
    @@ -1328,6 +1345,37 @@
         UpdateFile;
     end;
     
    +procedure TIniFile.ReadIniValues;
    +var
    +  slLines: TStringList;
    +begin
    +  FSectionList.Clear;
    +
    +  if FileExists(FFileName) then
    +  begin
    +    slLines := TStringList.Create;
    +    try
    +      // read the ini file values
    +      if FEncoding=nil then
    +        slLines.LoadFromFile(FFileName)
    +      else
    +      begin
    +        slLines.LoadFromFile(FFileName, FEncoding);
    +        if FEncoding <> slLines.Encoding then
    +        begin
    +          if FOwnsEncoding then
    +            FEncoding.Free;
    +          FEncoding := slLines.Encoding;
    +          FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
    +        end;
    +      end;
    +      FillSectionList(slLines);
    +    finally
    +      slLines.Free;
    +    end;
    +  end;
    +end;
    +
     { TMemIniFile }
     
     constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
    @@ -1372,20 +1420,11 @@
     end;
     
     procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
    -var
    -  slLines: TStringList;
     begin
       FFileName := AFileName;
       FStream := nil;
    -  if Reload then begin
    -    slLines := TStringList.Create;
    -    try
    -      slLines.LoadFromFile(FFileName);
    -      FillSectionList(slLines);
    -    finally
    -      slLines.Free;
    -    end;
    -  end;
    +  if Reload then
    +    ReadIniValues;
     end;
     
     procedure TMemIniFile.SetStrings(List: TStrings);
    
  • inifiles.pp-encoding-02.patch (6,293 bytes)
    Index: packages/fcl-base/src/inifiles.pp
    ===================================================================
    --- packages/fcl-base/src/inifiles.pp	(revision 37287)
    +++ packages/fcl-base/src/inifiles.pp	(working copy)
    @@ -153,8 +153,10 @@
       Private
         FBoolFalseStrings: TStringArray;
         FBoolTrueStrings: TStringArray;
    +    FEncoding: TEncoding;
         FFileName: string;
         FOptions: TIniFileOptions;
    +    FOwnsEncoding: Boolean;
         FSectionList: TIniFileSectionList;
         function GetOption(AIndex: TIniFileOption): Boolean;
         procedure SetOption(AIndex: TIniFileOption; AValue: Boolean);
    @@ -161,6 +163,8 @@
         procedure SetOptions(AValue: TIniFileOptions);
       public
         FormatSettings: TFormatSettings;
    +    constructor Create(const AFileName: string; ADefaultEncoding: TEncoding; AOptions : TIniFileOptions = []);
    +    constructor Create(const AFileName: string; ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions : TIniFileOptions = []);
         constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); virtual;
         constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); virtual;
         destructor Destroy; override;
    @@ -192,6 +196,7 @@
         procedure DeleteKey(const Section, Ident: String); virtual; abstract;
         procedure UpdateFile; virtual; abstract;
         function ValueExists(const Section, Ident: string): Boolean; virtual;
    +    property Encoding: TEncoding read FEncoding;
         property FileName: string read FFileName;
         Property Options : TIniFileOptions Read FOptions Write SetOptions;
         property EscapeLineFeeds: boolean index ifoEscapeLineFeeds Read GetOption ;deprecated 'Use options instead';
    @@ -200,6 +205,7 @@
         Property FormatSettingsActive : Boolean index ifoFormatSettingsActive Read GetOption Write SetOption;deprecated  'Use options instead';
         Property BoolTrueStrings : TStringArray Read FBoolTrueStrings Write FBoolTrueStrings;
         Property BoolFalseStrings : TStringArray Read FBoolFalseStrings Write FBoolFalseStrings;
    +    Property OwnsEncoding: Boolean Read FOwnsEncoding;
       end;
     
       { TIniFile }
    @@ -215,6 +221,7 @@
         Procedure MaybeDeleteSection(ASection : TIniFileSection);
         procedure SetCacheUpdates(const AValue: Boolean);
       protected
    +    procedure ReadIniValues;
         procedure MaybeUpdateFile;
         property Dirty : Boolean Read FDirty;
       public
    @@ -599,9 +606,29 @@
         Create(AFileName,[])
     end;
     
    +constructor TCustomIniFile.Create(const AFileName: string;
    +  ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean;
    +  AOptions: TIniFileOptions);
    +begin
    +  FEncoding := ADefaultEncoding;
    +  FOwnsEncoding := AOwnsEncoding;
    +  Create(AFileName, AOptions);
    +end;
    +
    +constructor TCustomIniFile.Create(const AFileName: string;
    +  ADefaultEncoding: TEncoding; AOptions: TIniFileOptions);
    +begin
    +  FEncoding := ADefaultEncoding;
    +  if FEncoding <> nil then
    +    FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
    +  Create(AFileName, AOptions);
    +end;
    +
     destructor TCustomIniFile.Destroy;
     begin
       FSectionList.Free;
    +  if FOwnsEncoding then
    +    FEncoding.Free;
       inherited Destroy;
     end;
     
    @@ -902,9 +929,7 @@
     { TIniFile }
     
     
    -constructor TIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
    -var
    -  slLines: TStringList;
    +constructor TIniFile.Create(const AFileName: string; AOptions: TIniFileoptions);
     begin
       FBOM := '';
       If Not (self is TMemIniFile) then
    @@ -911,17 +936,7 @@
         StripQuotes:=True;
       inherited Create(AFileName,AOptions);
       FStream := nil;
    -  slLines := TStringList.Create;
    -  try
    -    if FileExists(FFileName) then
    -      begin
    -      // read the ini file values
    -      slLines.LoadFromFile(FFileName);
    -      FillSectionList(slLines);
    -      end
    -  finally
    -    slLines.Free;
    -  end;
    +  ReadIniValues;
     end;
     
     constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean);
    @@ -933,7 +948,7 @@
         Create(AStream,[]);
     end;
     
    -constructor TIniFile.Create(AStream: TStream; AOptions : TIniFileOptions = []);
    +constructor TIniFile.Create(AStream: TStream; AOptions: TIniFileoptions);
     
     var
       slLines: TStringList;
    @@ -952,7 +967,7 @@
       end;
     end;
     
    -destructor TIniFile.destroy;
    +destructor TIniFile.Destroy;
     begin
       If FDirty and FCacheUpdates then
         try
    @@ -1233,7 +1248,7 @@
       ASection.Free;
     end;
     
    -Procedure TIniFile.MaybeDeleteSection(ASection : TIniFileSection);
    +procedure TIniFile.MaybeDeleteSection(ASection: TIniFileSection);
     
     begin
       If Asection.Empty then
    @@ -1306,7 +1321,10 @@
           If D <> '' Then
             if not ForceDirectories(D) then
               Raise EInoutError.CreateFmt(SErrCouldNotCreatePath,[D]);
    -      slLines.SaveToFile(FFileName);
    +      if FEncoding=nil then
    +        slLines.SaveToFile(FFileName)
    +      else
    +        slLines.SaveToFile(FFileName, FEncoding);
           end
         else if FStream <> nil then
           begin
    @@ -1328,6 +1346,38 @@
         UpdateFile;
     end;
     
    +procedure TIniFile.ReadIniValues;
    +var
    +  slLines: TStringList;
    +begin
    +  FSectionList.Clear;
    +
    +  if FileExists(FFileName) then
    +  begin
    +    slLines := TStringList.Create;
    +    try
    +      // read the ini file values
    +      if FEncoding=nil then
    +        slLines.LoadFromFile(FFileName)
    +      else
    +      begin
    +        slLines.DefaultEncoding := FEncoding;
    +        slLines.LoadFromFile(FFileName, nil);
    +        if FEncoding <> slLines.Encoding then
    +        begin
    +          if FOwnsEncoding then
    +            FEncoding.Free;
    +          FEncoding := slLines.Encoding;
    +          FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
    +        end;
    +      end;
    +      FillSectionList(slLines);
    +    finally
    +      slLines.Free;
    +    end;
    +  end;
    +end;
    +
     { TMemIniFile }
     
     constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
    @@ -1372,20 +1422,11 @@
     end;
     
     procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
    -var
    -  slLines: TStringList;
     begin
       FFileName := AFileName;
       FStream := nil;
    -  if Reload then begin
    -    slLines := TStringList.Create;
    -    try
    -      slLines.LoadFromFile(FFileName);
    -      FillSectionList(slLines);
    -    finally
    -      slLines.Free;
    -    end;
    -  end;
    +  if Reload then
    +    ReadIniValues;
     end;
     
     procedure TMemIniFile.SetStrings(List: TStrings);
    

Activities

Ondrej Pokorny

2017-09-21 14:43

developer  

inifiles.pp-encoding-01.patch (6,207 bytes)
Index: packages/fcl-base/src/inifiles.pp
===================================================================
--- packages/fcl-base/src/inifiles.pp	(revision 37292)
+++ packages/fcl-base/src/inifiles.pp	(working copy)
@@ -153,8 +153,10 @@
   Private
     FBoolFalseStrings: TStringArray;
     FBoolTrueStrings: TStringArray;
+    FEncoding: TEncoding;
     FFileName: string;
     FOptions: TIniFileOptions;
+    FOwnsEncoding: Boolean;
     FSectionList: TIniFileSectionList;
     function GetOption(AIndex: TIniFileOption): Boolean;
     procedure SetOption(AIndex: TIniFileOption; AValue: Boolean);
@@ -161,6 +163,8 @@
     procedure SetOptions(AValue: TIniFileOptions);
   public
     FormatSettings: TFormatSettings;
+    constructor Create(const AFileName: string; AEncoding: TEncoding; AOptions : TIniFileOptions = []);
+    constructor Create(const AFileName: string; AEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions : TIniFileOptions = []);
     constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); virtual;
     constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); virtual;
     destructor Destroy; override;
@@ -192,6 +196,7 @@
     procedure DeleteKey(const Section, Ident: String); virtual; abstract;
     procedure UpdateFile; virtual; abstract;
     function ValueExists(const Section, Ident: string): Boolean; virtual;
+    property Encoding: TEncoding read FEncoding;
     property FileName: string read FFileName;
     Property Options : TIniFileOptions Read FOptions Write SetOptions;
     property EscapeLineFeeds: boolean index ifoEscapeLineFeeds Read GetOption ;deprecated 'Use options instead';
@@ -200,6 +205,7 @@
     Property FormatSettingsActive : Boolean index ifoFormatSettingsActive Read GetOption Write SetOption;deprecated  'Use options instead';
     Property BoolTrueStrings : TStringArray Read FBoolTrueStrings Write FBoolTrueStrings;
     Property BoolFalseStrings : TStringArray Read FBoolFalseStrings Write FBoolFalseStrings;
+    Property OwnsEncoding: Boolean Read FOwnsEncoding;
   end;
 
   { TIniFile }
@@ -215,6 +221,7 @@
     Procedure MaybeDeleteSection(ASection : TIniFileSection);
     procedure SetCacheUpdates(const AValue: Boolean);
   protected
+    procedure ReadIniValues;
     procedure MaybeUpdateFile;
     property Dirty : Boolean Read FDirty;
   public
@@ -599,9 +606,28 @@
     Create(AFileName,[])
 end;
 
+constructor TCustomIniFile.Create(const AFileName: string;
+  AEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions: TIniFileOptions);
+begin
+  FEncoding := AEncoding;
+  FOwnsEncoding := AOwnsEncoding;
+  Create(AFileName, AOptions);
+end;
+
+constructor TCustomIniFile.Create(const AFileName: string;
+  AEncoding: TEncoding; AOptions: TIniFileOptions);
+begin
+  FEncoding := AEncoding;
+  if FEncoding <> nil then
+    FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
+  Create(AFileName, AOptions);
+end;
+
 destructor TCustomIniFile.Destroy;
 begin
   FSectionList.Free;
+  if FOwnsEncoding then
+    FEncoding.Free;
   inherited Destroy;
 end;
 
@@ -902,9 +928,7 @@
 { TIniFile }
 
 
-constructor TIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
-var
-  slLines: TStringList;
+constructor TIniFile.Create(const AFileName: string; AOptions: TIniFileoptions);
 begin
   FBOM := '';
   If Not (self is TMemIniFile) then
@@ -911,17 +935,7 @@
     StripQuotes:=True;
   inherited Create(AFileName,AOptions);
   FStream := nil;
-  slLines := TStringList.Create;
-  try
-    if FileExists(FFileName) then
-      begin
-      // read the ini file values
-      slLines.LoadFromFile(FFileName);
-      FillSectionList(slLines);
-      end
-  finally
-    slLines.Free;
-  end;
+  ReadIniValues;
 end;
 
 constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean);
@@ -933,7 +947,7 @@
     Create(AStream,[]);
 end;
 
-constructor TIniFile.Create(AStream: TStream; AOptions : TIniFileOptions = []);
+constructor TIniFile.Create(AStream: TStream; AOptions: TIniFileoptions);
 
 var
   slLines: TStringList;
@@ -952,7 +966,7 @@
   end;
 end;
 
-destructor TIniFile.destroy;
+destructor TIniFile.Destroy;
 begin
   If FDirty and FCacheUpdates then
     try
@@ -1233,7 +1247,7 @@
   ASection.Free;
 end;
 
-Procedure TIniFile.MaybeDeleteSection(ASection : TIniFileSection);
+procedure TIniFile.MaybeDeleteSection(ASection: TIniFileSection);
 
 begin
   If Asection.Empty then
@@ -1306,7 +1320,10 @@
       If D <> '' Then
         if not ForceDirectories(D) then
           Raise EInoutError.CreateFmt(SErrCouldNotCreatePath,[D]);
-      slLines.SaveToFile(FFileName);
+      if FEncoding=nil then
+        slLines.SaveToFile(FFileName)
+      else
+        slLines.SaveToFile(FFileName, FEncoding);
       end
     else if FStream <> nil then
       begin
@@ -1328,6 +1345,37 @@
     UpdateFile;
 end;
 
+procedure TIniFile.ReadIniValues;
+var
+  slLines: TStringList;
+begin
+  FSectionList.Clear;
+
+  if FileExists(FFileName) then
+  begin
+    slLines := TStringList.Create;
+    try
+      // read the ini file values
+      if FEncoding=nil then
+        slLines.LoadFromFile(FFileName)
+      else
+      begin
+        slLines.LoadFromFile(FFileName, FEncoding);
+        if FEncoding <> slLines.Encoding then
+        begin
+          if FOwnsEncoding then
+            FEncoding.Free;
+          FEncoding := slLines.Encoding;
+          FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
+        end;
+      end;
+      FillSectionList(slLines);
+    finally
+      slLines.Free;
+    end;
+  end;
+end;
+
 { TMemIniFile }
 
 constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
@@ -1372,20 +1420,11 @@
 end;
 
 procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
-var
-  slLines: TStringList;
 begin
   FFileName := AFileName;
   FStream := nil;
-  if Reload then begin
-    slLines := TStringList.Create;
-    try
-      slLines.LoadFromFile(FFileName);
-      FillSectionList(slLines);
-    finally
-      slLines.Free;
-    end;
-  end;
+  if Reload then
+    ReadIniValues;
 end;
 
 procedure TMemIniFile.SetStrings(List: TStrings);

Ondrej Pokorny

2017-09-23 03:01

developer  

inifiles.pp-encoding-02.patch (6,293 bytes)
Index: packages/fcl-base/src/inifiles.pp
===================================================================
--- packages/fcl-base/src/inifiles.pp	(revision 37287)
+++ packages/fcl-base/src/inifiles.pp	(working copy)
@@ -153,8 +153,10 @@
   Private
     FBoolFalseStrings: TStringArray;
     FBoolTrueStrings: TStringArray;
+    FEncoding: TEncoding;
     FFileName: string;
     FOptions: TIniFileOptions;
+    FOwnsEncoding: Boolean;
     FSectionList: TIniFileSectionList;
     function GetOption(AIndex: TIniFileOption): Boolean;
     procedure SetOption(AIndex: TIniFileOption; AValue: Boolean);
@@ -161,6 +163,8 @@
     procedure SetOptions(AValue: TIniFileOptions);
   public
     FormatSettings: TFormatSettings;
+    constructor Create(const AFileName: string; ADefaultEncoding: TEncoding; AOptions : TIniFileOptions = []);
+    constructor Create(const AFileName: string; ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean; AOptions : TIniFileOptions = []);
     constructor Create(const AFileName: string; AOptions : TIniFileOptions = []); virtual;
     constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean); virtual;
     destructor Destroy; override;
@@ -192,6 +196,7 @@
     procedure DeleteKey(const Section, Ident: String); virtual; abstract;
     procedure UpdateFile; virtual; abstract;
     function ValueExists(const Section, Ident: string): Boolean; virtual;
+    property Encoding: TEncoding read FEncoding;
     property FileName: string read FFileName;
     Property Options : TIniFileOptions Read FOptions Write SetOptions;
     property EscapeLineFeeds: boolean index ifoEscapeLineFeeds Read GetOption ;deprecated 'Use options instead';
@@ -200,6 +205,7 @@
     Property FormatSettingsActive : Boolean index ifoFormatSettingsActive Read GetOption Write SetOption;deprecated  'Use options instead';
     Property BoolTrueStrings : TStringArray Read FBoolTrueStrings Write FBoolTrueStrings;
     Property BoolFalseStrings : TStringArray Read FBoolFalseStrings Write FBoolFalseStrings;
+    Property OwnsEncoding: Boolean Read FOwnsEncoding;
   end;
 
   { TIniFile }
@@ -215,6 +221,7 @@
     Procedure MaybeDeleteSection(ASection : TIniFileSection);
     procedure SetCacheUpdates(const AValue: Boolean);
   protected
+    procedure ReadIniValues;
     procedure MaybeUpdateFile;
     property Dirty : Boolean Read FDirty;
   public
@@ -599,9 +606,29 @@
     Create(AFileName,[])
 end;
 
+constructor TCustomIniFile.Create(const AFileName: string;
+  ADefaultEncoding: TEncoding; AOwnsEncoding: Boolean;
+  AOptions: TIniFileOptions);
+begin
+  FEncoding := ADefaultEncoding;
+  FOwnsEncoding := AOwnsEncoding;
+  Create(AFileName, AOptions);
+end;
+
+constructor TCustomIniFile.Create(const AFileName: string;
+  ADefaultEncoding: TEncoding; AOptions: TIniFileOptions);
+begin
+  FEncoding := ADefaultEncoding;
+  if FEncoding <> nil then
+    FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
+  Create(AFileName, AOptions);
+end;
+
 destructor TCustomIniFile.Destroy;
 begin
   FSectionList.Free;
+  if FOwnsEncoding then
+    FEncoding.Free;
   inherited Destroy;
 end;
 
@@ -902,9 +929,7 @@
 { TIniFile }
 
 
-constructor TIniFile.Create(const AFileName: string; AOptions : TIniFileOptions = []);
-var
-  slLines: TStringList;
+constructor TIniFile.Create(const AFileName: string; AOptions: TIniFileoptions);
 begin
   FBOM := '';
   If Not (self is TMemIniFile) then
@@ -911,17 +936,7 @@
     StripQuotes:=True;
   inherited Create(AFileName,AOptions);
   FStream := nil;
-  slLines := TStringList.Create;
-  try
-    if FileExists(FFileName) then
-      begin
-      // read the ini file values
-      slLines.LoadFromFile(FFileName);
-      FillSectionList(slLines);
-      end
-  finally
-    slLines.Free;
-  end;
+  ReadIniValues;
 end;
 
 constructor TIniFile.Create(AStream: TStream; AEscapeLineFeeds : Boolean);
@@ -933,7 +948,7 @@
     Create(AStream,[]);
 end;
 
-constructor TIniFile.Create(AStream: TStream; AOptions : TIniFileOptions = []);
+constructor TIniFile.Create(AStream: TStream; AOptions: TIniFileoptions);
 
 var
   slLines: TStringList;
@@ -952,7 +967,7 @@
   end;
 end;
 
-destructor TIniFile.destroy;
+destructor TIniFile.Destroy;
 begin
   If FDirty and FCacheUpdates then
     try
@@ -1233,7 +1248,7 @@
   ASection.Free;
 end;
 
-Procedure TIniFile.MaybeDeleteSection(ASection : TIniFileSection);
+procedure TIniFile.MaybeDeleteSection(ASection: TIniFileSection);
 
 begin
   If Asection.Empty then
@@ -1306,7 +1321,10 @@
       If D <> '' Then
         if not ForceDirectories(D) then
           Raise EInoutError.CreateFmt(SErrCouldNotCreatePath,[D]);
-      slLines.SaveToFile(FFileName);
+      if FEncoding=nil then
+        slLines.SaveToFile(FFileName)
+      else
+        slLines.SaveToFile(FFileName, FEncoding);
       end
     else if FStream <> nil then
       begin
@@ -1328,6 +1346,38 @@
     UpdateFile;
 end;
 
+procedure TIniFile.ReadIniValues;
+var
+  slLines: TStringList;
+begin
+  FSectionList.Clear;
+
+  if FileExists(FFileName) then
+  begin
+    slLines := TStringList.Create;
+    try
+      // read the ini file values
+      if FEncoding=nil then
+        slLines.LoadFromFile(FFileName)
+      else
+      begin
+        slLines.DefaultEncoding := FEncoding;
+        slLines.LoadFromFile(FFileName, nil);
+        if FEncoding <> slLines.Encoding then
+        begin
+          if FOwnsEncoding then
+            FEncoding.Free;
+          FEncoding := slLines.Encoding;
+          FOwnsEncoding := not TEncoding.IsStandardEncoding(FEncoding);
+        end;
+      end;
+      FillSectionList(slLines);
+    finally
+      slLines.Free;
+    end;
+  end;
+end;
+
 { TMemIniFile }
 
 constructor TMemIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
@@ -1372,20 +1422,11 @@
 end;
 
 procedure TMemIniFile.Rename(const AFileName: string; Reload: Boolean);
-var
-  slLines: TStringList;
 begin
   FFileName := AFileName;
   FStream := nil;
-  if Reload then begin
-    slLines := TStringList.Create;
-    try
-      slLines.LoadFromFile(FFileName);
-      FillSectionList(slLines);
-    finally
-      slLines.Free;
-    end;
-  end;
+  if Reload then
+    ReadIniValues;
 end;
 
 procedure TMemIniFile.SetStrings(List: TStrings);

Ondrej Pokorny

2017-09-23 03:02

developer   ~0102991

02.patch: use DefaultEncoding instead of AEncoding parameter for the string list so that BOM is correctly detected.

Ondrej Pokorny

2017-11-08 18:18

developer   ~0103962

Ping: Michael, are there any problems with the second patch?

If you don't like to have encoding support in TIniFile I assume I can prepare another patch that would just offer a virtual ReadIniValues and virtual WriteIniValues so that I could override them in my own TIniFile-descendant.

(Encoding support is crucial for Lazarus win32 applications where INI files are usually ANSI but the LCL needs UTF-8.)

Michael Van Canneyt

2017-12-29 11:22

administrator   ~0105094

No problem with the patch.

Problem is in available time :)

Checked and applied the patch, thank you very much !

Ondrej Pokorny

2017-12-30 10:45

developer   ~0105136

Thank you!

Issue History

Date Modified Username Field Change
2017-09-21 14:43 Ondrej Pokorny New Issue
2017-09-21 14:43 Ondrej Pokorny File Added: inifiles.pp-encoding-01.patch
2017-09-23 03:01 Ondrej Pokorny File Added: inifiles.pp-encoding-02.patch
2017-09-23 03:02 Ondrej Pokorny Note Added: 0102991
2017-09-26 17:16 Michael Van Canneyt Assigned To => Michael Van Canneyt
2017-09-26 17:16 Michael Van Canneyt Status new => assigned
2017-11-08 18:18 Ondrej Pokorny Note Added: 0103962
2017-12-29 11:22 Michael Van Canneyt Fixed in Revision => 37852
2017-12-29 11:22 Michael Van Canneyt Note Added: 0105094
2017-12-29 11:22 Michael Van Canneyt Status assigned => resolved
2017-12-29 11:22 Michael Van Canneyt Fixed in Version => 3.1.1
2017-12-29 11:22 Michael Van Canneyt Resolution open => fixed
2017-12-29 11:22 Michael Van Canneyt Target Version => 3.2.0
2017-12-30 10:45 Ondrej Pokorny Note Added: 0105136
2017-12-30 10:45 Ondrej Pokorny Status resolved => closed