Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Making inifiles cross-platform



Permlink Replies: 15 - Last Post: Jul 30, 2017 2:50 AM Last Post By: René Marijnissen
Scott Pinkham

Posts: 54
Registered: 3/19/07
Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 25, 2017 8:08 PM
I have a cross-platform app that uses INI files to store some settings (using TIniFile class to read/write the files). I need to be able to copy these files between systems, but any files INI that are created in the Android version of the app have each line terminated with just a single line feed (LF), which means they don't work when trying to read them in Windows since Windows expects each line to be terminated with a carriage return (CR) and a line feed (LF). Is there a simple way to ether get the Android version to terminate lines in the ini files with a CR LF, or get Windows to accept a file with just LFs?

TIA,
Scott

PS. I'm using Delphi 10.1 Berlin.
Dave Nottage

Posts: 1,850
Registered: 1/7/00
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 25, 2017 8:43 PM   in response to: Scott Pinkham in response to: Scott Pinkham
I have a cross-platform app that uses INI files to store some settings (using TIniFile class to read/write the files). I need to be able to copy these files between systems, but any
files INI that are created in the Android version of the app have each line terminated with just a single line feed (LF), which means they don't work when trying to read them in
Windows since Windows expects each line to be terminated with a carriage return (CR) and a line feed (LF). Is there a simple way to ether get the Android version to terminate
lines in the ini files with a CR LF, or get Windows to accept a file with just LFs?

Personally, I store configuration files in JSON format, then it doesn't (or shouldn't) matter. I recommend watching this:

https://www.youtube.com/watch?v=e6IKO8so-Is

--
Dave Nottage [TeamB]
Find hints tips and tricks at Delphi Worlds blog: http://www.delphiworlds.com

Scott Pinkham

Posts: 54
Registered: 3/19/07
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 26, 2017 8:20 AM   in response to: Dave Nottage in response to: Dave Nottage
Dave Nottage wrote:
I have a cross-platform app that uses INI files to store some settings (using TIniFile class to read/write the files). I need to be able to copy these files between systems, but any
files INI that are created in the Android version of the app have each line terminated with just a single line feed (LF), which means they don't work when trying to read them in
Windows since Windows expects each line to be terminated with a carriage return (CR) and a line feed (LF). Is there a simple way to ether get the Android version to terminate
lines in the ini files with a CR LF, or get Windows to accept a file with just LFs?

Personally, I store configuration files in JSON format, then it doesn't (or shouldn't) matter. I recommend watching this:

https://www.youtube.com/watch?v=e6IKO8so-Is

--
Dave Nottage [TeamB]
Find hints tips and tricks at Delphi Worlds blog: http://www.delphiworlds.com


If I could go back in time I'd probably use JSON format, but we already have customers that have generated lots of files in the INI format, so we need to maintain compatibility. From time-to-time we have the need to manually edit the files too, and INI files are more human-friendly (JSON isn't hard, it's just that it's easy for a human to mess up and get unmatched braces).
-Scott
René Marijnissen

Posts: 6
Registered: 10/7/16
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 26, 2017 11:04 AM   in response to: Dave Nottage in response to: Dave Nottage
Dave Nottage wrote:
I have a cross-platform app that uses INI files to store some settings (using TIniFile class to read/write the files). I need to be able to copy these files between systems, but any
files INI that are created in the Android version of the app have each line terminated with just a single line feed (LF), which means they don't work when trying to read them in
Windows since Windows expects each line to be terminated with a carriage return (CR) and a line feed (LF). Is there a simple way to ether get the Android version to terminate
lines in the ini files with a CR LF, or get Windows to accept a file with just LFs?

Personally, I store configuration files in JSON format, then it doesn't (or shouldn't) matter. I recommend watching this:

https://www.youtube.com/watch?v=e6IKO8so-Is

--
Dave Nottage [TeamB]
Find hints tips and tricks at Delphi Worlds blog: http://www.delphiworlds.com


Hello Dave,

I also want to use JSON file instead of ini file's, and i also watched the video "JSON: The New INI File with Jeff Lefebvre", but it's going to fast for me.

Is the source code for this project somewehere available, i have been searching for over 2 day now....with no luck.

Thnx in advance,
René
Dave Nottage

Posts: 1,850
Registered: 1/7/00
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 26, 2017 3:02 PM   in response to: René Marijnissen in response to: René Marijnissen
René Marijnissen wrote:

Is the source code for this project somewehere available, i have been searching for over 2 day now....with no luck.

The source code is here:

http://thedailydeveloper.com/skillsprint14-json/

Jeff mentioned his website in the first few seconds of the video.

Incidentally, I don't exactly use his source code; just the principle. My preference is to create classes that
represent the configuration, then just use TJson.JsonToObject for loading and TJson.ObjectToJsonString for saving. It's
mind-blowingly simple and consequently way less work than managing INI files.

For reading/writing the JSON from/to files, I use TFile.ReadAllText and TFile.WriteAllText, which may be what Jeff uses.

--
Dave Nottage [MVP, TeamB]
Find hints, tips and tricks at Delphi Worlds blog: http://www.delphiworlds.com
René Marijnissen

Posts: 6
Registered: 10/7/16
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 27, 2017 9:54 AM   in response to: Dave Nottage in response to: Dave Nottage
Dave Nottage wrote:
The source code is here:

http://thedailydeveloper.com/skillsprint14-json/

Incidentally, I don't exactly use his source code; just the principle. My preference is to create classes that
represent the configuration, then just use TJson.JsonToObject for loading and TJson.ObjectToJsonString for saving. It's mind-blowingly simple and consequently way less work than managing INI files.

For reading/writing the JSON from/to files, I use TFile.ReadAllText and TFile.WriteAllText, which may be what Jeff uses.

Thanks for the reply Dave,

I'am working on an vcl application which needs to write the contents of 1 or 2 textboxes to
a json configuration file.

Is it possible that you give me a small example of code that make this work.?

Thanks in advance,
René

Edited by: René Marijnissen on Jul 27, 2017 9:56 AM

Edited by: René Marijnissen on Jul 27, 2017 10:01 AM
Dave Nottage

Posts: 1,850
Registered: 1/7/00
Re: Making inifiles cross-platform [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 27, 2017 4:57 PM   in response to: René Marijnissen in response to: René Marijnissen
René Marijnissen wrote:

Is it possible that you give me a small example of code that make this work.?

Sure:


uses
  System.SysUtils, System.IOUtils, REST.Json;
 
// Configuration object
type
  TSomeConfig = class(TObject)
  private
    FSomeString: string;
    FSomeInteger: Integer;
  public
    property SomeString: string read FSomeString write FSomeString;
    property SomeInteger: string read FSomeInteger write FSomeInteger;
  end;
 
...
 
// Reading
var
  LJSON: string;
begin
  LJSON := TFile.ReadAllText('SomeConfigFile.json');
  if not LJSON.IsEmpty then
    FSomeConfig := TJson.JsonToObject<TSomeConfig>(LJSON)
  else
    FSomeConfig := TSomeConfig.Create;  
end;
 
// Writing
 
  TFile.WriteAllText('SomeConfigFile.json', TJson.ObjectToJsonString(FSomeConfig));
 


That's a pretty basic example; you can actually nest arrays and objects ect within objects. When I have time, I'll
write something more complete, however you can see an example of reading more complex JSON into objects at the demo,
here:

http://delphiworlds.com/2017/05/cross-platform-delphi-easy/

There's a description of the JSON at the beginning of the article; links to the source are at the beginning and end.

--
Dave Nottage [MVP, TeamB]
Find hints, tips and tricks at Delphi Worlds blog: http://www.delphiworlds.com
René Marijnissen

Posts: 6
Registered: 10/7/16
Re: Making inifiles cross-platform [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 28, 2017 11:16 AM   in response to: Dave Nottage in response to: Dave Nottage
Dave Nottage wrote:
René Marijnissen wrote:

Is it possible that you give me a small example of code that make this work.?

Sure:

 
uses
  System.SysUtils, System.IOUtils, REST.Json;
 
// Configuration object
type
  TSomeConfig = class(TObject)
  private
    FSomeString: string;
    FSomeInteger: Integer;
  public
    property SomeString: string read FSomeString write FSomeString;
    property SomeInteger: string read FSomeInteger write FSomeInteger;
  end;
 
...
 
// Reading
var
  LJSON: string;
begin
  LJSON := TFile.ReadAllText('SomeConfigFile.json');
  if not LJSON.IsEmpty then
    FSomeConfig := TJson.JsonToObject<TSomeConfig>(LJSON)
  else
    FSomeConfig := TSomeConfig.Create;  
end;
 
// Writing
 
  TFile.WriteAllText('SomeConfigFile.json', TJson.ObjectToJsonString(FSomeConfig));
 


Dave,

Thanx for the quick reply, but i get an error "Undeclared identifier 'FSomestring' ??

THnx,
René
Dave Nottage

Posts: 1,850
Registered: 1/7/00
Re: Making inifiles cross-platform [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 28, 2017 3:03 PM   in response to: René Marijnissen in response to: René Marijnissen
René Marijnissen wrote:

Thanx for the quick reply, but i get an error "Undeclared identifier 'FSomestring' ??

I don't see how, because it's right there in the code. Please show your code that does not compile.

--
Dave Nottage [MVP, TeamB]
Find hints, tips and tricks at Delphi Worlds blog: http://www.delphiworlds.com
René Marijnissen

Posts: 6
Registered: 10/7/16
Re: Making inifiles cross-platform [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 29, 2017 1:43 AM   in response to: Dave Nottage in response to: Dave Nottage
Dave Nottage wrote:

I don't see how, because it's right there in the code. Please show your code that does not compile.

--
Dave,

Below the code which generates the Undeclared identifier 'FSomeString'

unit uMain;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,Vcl.StdCtrls,
  REST.Json,System.IOUtils;
 
type
  TSomeConfig = class(TObject)
  private
    FSomeString: string;
  public
    property SomeString: string read FSomeString write FSomeString;
  end;
 
type
  TForm1 = class(TForm)
    edt1: TEdit;
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.FormCreate(Sender: TObject);
var
LJSON, strings, Filepath : string;
begin
  if Filepath = '' then
    Filepath := ParamStr(0) + '.json';
  LJSON := TFile.ReadAllText(Filepath);
  if not LJSON.IsEmpty then
    FSomeString := TJson.JsonToObject<TSomeConfig>(LJSON)
  else
    FSomeString := TSomeConfig.Create;
end;
end.
Rudy Velthuis (...


Posts: 7,731
Registered: 9/22/99
Re: Making inifiles cross-platform [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 29, 2017 2:17 AM   in response to: René Marijnissen in response to: René Marijnissen
René Marijnissen wrote:

Dave Nottage wrote:

I don't see how, because it's right there in the code. Please show
your code that does not compile.

--
Dave,

Below the code which generates the Undeclared identifier 'FSomeString'

unit uMain;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,   Vcl.Controls, Vcl.Forms,
Vcl.Dialogs,Vcl.StdCtrls,   REST.Json,System.IOUtils;
 
type
  TSomeConfig = class(TObject)
  private
    FSomeString: string;
  public
    property SomeString: string read FSomeString write FSomeString;
  end;
 
type
  TForm1 = class(TForm)
    edt1: TEdit;
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.FormCreate(Sender: TObject);
var
LJSON, strings, Filepath : string;
begin
  if Filepath = '' then
    Filepath := ParamStr(0) + '.json';
  LJSON := TFile.ReadAllText(Filepath);
  if not LJSON.IsEmpty then
    FSomeString := TJson.JsonToObject<TSomeConfig>(LJSON)
  else
    FSomeString := TSomeConfig.Create;
end;
end.

FSomeString is a private member of TSomeConfig, not of the TForm1, so
in the scope of TForm1 it is not known indeed.

Second, you can't assign a TSomeConfig to a string either. Your code
makes no sense.

This would make sense:

procedure TForm1.FormCreate(Sender: TObject);
var
  LJSON, Filepath : string;
  SomeConf: TSomeConfig;
  SomeString: string;
begin
  Filepath := ParamStr(0) + '.json';
  LJSON := TFile.ReadAllText(Filepath);
  if not LJSON.IsEmpty then
    SomeConf := TJson.JsonToObject<TSomeConfig>(LJSON)
  else
    SomeConf := TSomeConfig.Create;
  if Assigned(SomeConf) then
    SomeString := SomeConf.FSomeString;
  // etc...
end;

--
Rudy Velthuis http://www.rvelthuis.de

"The hardest years in life are those between ten and seventy."
-- Helen Hayes, age 83

René Marijnissen

Posts: 6
Registered: 10/7/16
Re: Making inifiles cross-platform [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 30, 2017 2:50 AM   in response to: Rudy Velthuis (... in response to: Rudy Velthuis (...
Rudy Velthuis (TeamB, MVP) wrote:

FSomeString is a private member of TSomeConfig, not of the TForm1, so
in the scope of TForm1 it is not known indeed.

Second, you can't assign a TSomeConfig to a string either. Your code
makes no sense.

This would make sense:

procedure TForm1.FormCreate(Sender: TObject);
var
  LJSON, Filepath : string;
  SomeConf: TSomeConfig;
  SomeString: string;
begin
  Filepath := ParamStr(0) + '.json';
  LJSON := TFile.ReadAllText(Filepath);
  if not LJSON.IsEmpty then
    SomeConf := TJson.JsonToObject<TSomeConfig>(LJSON)
  else
    SomeConf := TSomeConfig.Create;
  if Assigned(SomeConf) then
    SomeString := SomeConf.FSomeString;
  // etc...
end;

--
Rudy Velthuis http://www.rvelthuis.de


Rudy,
Thanks for the reply, the program is working now !!

Kind regards,
René
René Marijnissen

Posts: 6
Registered: 10/7/16
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 27, 2017 12:04 PM   in response to: Dave Nottage in response to: Dave Nottage
Incidentally, I don't exactly use his source code; just the principle. My preference is to create classes that
represent the configuration, then just use TJson.JsonToObject for loading and TJson.ObjectToJsonString for saving. It's
mind-blowingly simple and consequently way less work than managing INI files.

For reading/writing the JSON from/to files, I use TFile.ReadAllText and TFile.WriteAllText

Dave,

I made a small program with an edit box and a button on a form.

It works but with errors. When i start the program for the first time i can press the button and generate the json file.
When i start the program for the second time i get an error "Internal: Cannot instantiate type uMain.TConfigurationSettings"

Second question,
how can i write the text from the edit box to the json file??

Below the code until so far.

unit uMain;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
 
type
  TformMain = class(TForm)
    edt1: TEdit;
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  formMain: TformMain;
 
  implementation
 
{$R *.dfm}
uses
REST.Json;
 
type
  TConfigurationSettings = class (TObject)
  private
    FDrawingLocation : string ;
    procedure SetDrawingLocation (const Value: string);
  public
    property DrawingLocation: string read FDrawingLocation write SetDrawingLocation;
//    constructor create();
//    constructor destroy();
  published
    class function LoadFromFile(filePath : string = '') : TConfigurationSettings;
    class procedure SaveToFile(Settings : TConfigurationSettings; filePath : string = '');
end;
var
  Settings : TConfigurationSettings;
 
{ TConfigurationSettings }
class function TConfigurationSettings.LoadFromFile(
  filePath: string): TConfigurationSettings;
var
  strings : TStrings;
begin
  if filePath = '' then
      filePath := ParamStr(0) + '.json';
  if FileExists(filePath) then
  begin
    strings := TStringList.Create;
    strings.LoadFromFile(filePath);
    Result:= TJson.JsonToObject<TConfigurationSettings>(strings.Text);
    strings.Free();
  end
  else
  Result := TConfigurationSettings.Create();
end;
 
class procedure TConfigurationSettings.SaveToFile(
  Settings: TConfigurationSettings; filePath: string ='');
var
  strings : TStrings;
begin
  if filePath = '' then
      filePath := ParamStr(0) + '.json';
  strings := TStringList.Create;
  strings.Add(TJson.ObjectToJsonString(Settings));
  strings.SaveToFile(filePath);
  strings.Free;
end;
 
procedure TConfigurationSettings.SetDrawingLocation(const Value: string);
begin
 FDrawingLocation := Value;
end;
 
procedure TformMain.btn1Click(Sender: TObject);
begin
  TConfigurationSettings.SaveToFile(Settings);
end;
 
procedure TformMain.FormCreate(Sender: TObject);
begin
  Settings := TConfigurationSettings.LoadFromFile();
end;
 
end.
 
 
Peter Below

Posts: 1,227
Registered: 12/16/99
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 25, 2017 11:48 PM   in response to: Scott Pinkham in response to: Scott Pinkham
Scott Pinkham wrote:

I have a cross-platform app that uses INI files to store some
settings (using TIniFile class to read/write the files). I need to be
able to copy these files between systems, but any files INI that are
created in the Android version of the app have each line terminated
with just a single line feed (LF), which means they don't work when
trying to read them in Windows since Windows expects each line to be
terminated with a carriage return (CR) and a line feed (LF). Is there
a simple way to ether get the Andr oid version to terminate lines in
the ini files with a CR LF, or get Windows to accept a file with just
LFs?

TIA,
Scott

PS. I'm using Delphi 10.1 Berlin.

Use TMemInifile instead of TInifile. The interface is the same, so no
code changes other than changing the class is necessary, and the
implementation does not rely on OS support.

--
Peter Below
TeamB
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 26, 2017 11:08 AM   in response to: Peter Below in response to: Peter Below
Peter Below wrote:

so no code changes other than changing the class is necessary

That is not entirely true. When modifying an INI file, TIniFile
automatically saves any pending changes to file when it is destructed.
TMemIniFile does not do that. You have to call its UpdateFile() method
explicitly, or else your changes will be lost.

--
Remy Lebeau (TeamB)
Scott Pinkham

Posts: 54
Registered: 3/19/07
Re: Making inifiles cross-platform
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 26, 2017 5:24 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Peter Below wrote:

so no code changes other than changing the class is necessary

That is not entirely true. When modifying an INI file, TIniFile
automatically saves any pending changes to file when it is destructed.
TMemIniFile does not do that. You have to call its UpdateFile() method
explicitly, or else your changes will be lost.

--
Remy Lebeau (TeamB)

To close the loop on this:

I implemented my file save/recall using a new class derived from TMemIniFile that has an override of the destructor to call UpdateFile(). That way, all I had to do was change all instances of TIniFile to my custom class and not add calls to UpdateFile everywhere.

TMemIniFile seems to work fine on all platforms, and will read/write files with both CR and/or CRLF terminators, so the files can be copied between different platforms and still work.

Thanks for all the help!
-Scott
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02