Watch, Follow, &
Connect with Us

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


Welcome, Guest
Guest Settings
Help

Thread: Is there a way to sort the sections of an inifile?



Permlink Replies: 9 - Last Post: May 7, 2015 11:54 PM Last Post By: Rudy Velthuis (...
Bo Berglund

Posts: 757
Registered: 10/23/02
Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 6, 2015 4:26 AM
I keep language strings in an ini file structure to enable quick
switching of languages in my VCL application.
Each form has its own section in the ini file with the property as the
identifier.
Example:
[TfrmMain]
Caption=My main form caption
btnOpen.Caption=Open
btnClose.Caption=Close
lblName.Caption=Name
etc, etc


When translating this it is very inconvenient to have the components
spread out over the section in random order, so I would like the items
in each section to be sorted alphabetically so the above would look
like this:
[TfrmMain]
btnClose.Caption=Close
btnOpen.Caption=Open
Caption=My main form caption
lblName.Caption=Name


All interactions with te language files are done via a TCustomIniFile
descendant, in this case TMemIniFile.

I would like to know if there is any way to have the object sort the
section content when reading/writing from/to the file??

Currently I have to manually use UltraEdit to sort the section
contents every time a new file is created and it is a tedious and
error prone process given that the language files contain 1000-2000
items in 30-50 sections....

I use (from a few months back) RAD Studio XE5 and the language system
is used both in Delphi and C++Builder.

---
Bo Berglund
Sweden & Texas
Newsreader: Forte Free Agent 1.92/32.572

Roy Lambert

Posts: 1,063
Registered: 8/7/01
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 6, 2015 5:12 AM   in response to: Bo Berglund in response to: Bo Berglund
Bo

You could write a custom sort routine, but I suspect that it would be a bit nasty, or you could go down the inifile, read each section into a sorted stringlist and then write it back.

Roy Lambert

Alex Belo

Posts: 626
Registered: 10/8/06
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 6, 2015 5:25 AM   in response to: Bo Berglund in response to: Bo Berglund
Bo Berglund wrote:

I would like to know if there is any way to have the object sort the
section content when reading/writing from/to the file??

Writing is enough, isn't it?

In 2007:

TCustomIniFile
...
procedure UpdateFile; virtual; abstract;

TMemIniFile
...
procedure UpdateFile; override;

Code Is simple:

procedure TMemIniFile.UpdateFile;
var
List: TStringList;
begin
List := TStringList.Create;
try
GetStrings(List);
List.SaveToFile(FFileName);
finally
List.Free;
end;
end;

OK, now GetStrings (used only in UpdateFile):

procedure TMemIniFile.GetStrings(List: TStrings);
var
I, J: Integer;
Strings: TStrings;
begin
List.BeginUpdate;
try
for I := 0 to FSections.Count - 1 do
begin
List.Add('[' + FSections[I] + ']');
Strings := TStrings(FSections.Objects[I]);
for J := 0 to Strings.Count - 1 do List.Add(Strings[J]);
List.Add('');
end;
finally
List.EndUpdate;
end;
end;

Unfortunately GetStrings is not virtual.

So you can create TMemIniFile descendant with new version of UpdateFile
which calls GetSortedStrings (untested):

procedure TMemSortedIniFile.UpdateFile;
var
List: TStringList;
begin
List := TStringList.Create;
try
GetSortedStrings(List);
List.SaveToFile(FFileName);
finally
List.Free;
end;
end;

procedure TMemSortedIniFile.GetSortedStrings(List: TStrings);
var
I, J: Integer;
Strings: TStrings;
buf: TStringList;
begin
buf:=nil;
List.BeginUpdate;
try
buf:=TStringList.Create;
for I := 0 to FSections.Count - 1 do
begin
List.Add('[' + FSections[I] + ']');
Strings := TStrings(FSections.Objects[I]);
buf.Assign(Strings);
buf.Sort;
for J := 0 to buf.Count - 1 do List.Add(buf[J]);
List.Add('');
end;
finally
buf.Free;
List.EndUpdate;
end;
end;

--
Alex
Bo Berglund

Posts: 757
Registered: 10/23/02
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 6, 2015 5:59 AM   in response to: Alex Belo in response to: Alex Belo
On Wed, 6 May 2015 05:25:35 -0700, Alex Belo <b dot a dot v at inbox dot ru> wrote:

Bo Berglund wrote:

I would like to know if there is any way to have the object sort the
section content when reading/writing from/to the file??

Writing is enough, isn't it?

Yes, of course. My bad...


OK, now GetStrings (used only in UpdateFile):

procedure TMemIniFile.GetStrings(List: TStrings);
var
I, J: Integer;
Strings: TStrings;
begin
List.BeginUpdate;
try
for I := 0 to FSections.Count - 1 do
begin
List.Add('[' + FSections[I] + ']');
Strings := TStrings(FSections.Objects[I]);
for J := 0 to Strings.Count - 1 do List.Add(Strings[J]);
List.Add('');
end;
finally
List.EndUpdate;
end;
end;

Unfortunately GetStrings is not virtual.

So you can create TMemIniFile descendant with new version of UpdateFile
which calls GetSortedStrings (untested):

procedure TMemSortedIniFile.UpdateFile;
var
List: TStringList;
begin
List := TStringList.Create;
try
GetSortedStrings(List);
List.SaveToFile(FFileName);
finally
List.Free;
end;
end;

procedure TMemSortedIniFile.GetSortedStrings(List: TStrings);
var
I, J: Integer;
Strings: TStrings;
buf: TStringList;
begin
buf:=nil;
List.BeginUpdate;
try
buf:=TStringList.Create;
for I := 0 to FSections.Count - 1 do
begin
List.Add('[' + FSections[I] + ']');
Strings := TStrings(FSections.Objects[I]);
buf.Assign(Strings);
buf.Sort; // <== Did not know about this!!!!!
for J := 0 to buf.Count - 1 do List.Add(buf[J]);
List.Add('');
end;
finally
buf.Free;
List.EndUpdate;
end;
end;

Thanks,
I don't want to mess too much with the TMemIniFile and create a
descendant of it for this purpose...
But I will have a look at this suggestion when I revisit the language
string exctractor code. This piece of software (not mine) scans all
forms in the application for certain types of components with captions
and other visual texts and creates a language master file using the
TMemIniFile. But I might as well do away with that altogether and
build the language master only using TStringLists.

And I did not know there was a TStringList.Sort method available,
always thought one had to create the list with properties sorted and
no duplicates to make it sort.
With this new (for me) info it seems like a doable thing to handle it
without messing with the ini files.
Thanks for the explanations!

---
Bo Berglund
Sweden & Texas
Newsreader: Forte Free Agent 1.92/32.572

Alex Belo

Posts: 626
Registered: 10/8/06
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 6, 2015 6:31 AM   in response to: Bo Berglund in response to: Bo Berglund
Bo Berglund wrote:

I don't want to mess too much with the TMemIniFile and create a
descendant of it for this purpose...
But I will have a look at this suggestion when I revisit the language
string exctractor code. This piece of software (not mine) scans all
forms in the application for certain types of components with captions
and other visual texts and creates a language master file using the
TMemIniFile.

So you probably must replace TMemIniFile on new sorted version only in
one or two files. ;-)

And I did not know there was a TStringList.Sort method available,
always thought one had to create the list with properties sorted and
no duplicates to make it sort.

If Sorted=true the list will be sorted on every string insertion (very
ineffective in case of big lists especially taking into account that
Delphi lists actually are arrays and insertion moves 'tail' of array on
"one position" in memory).

--
Alex
Bo Berglund

Posts: 757
Registered: 10/23/02
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 6, 2015 8:07 AM   in response to: Alex Belo in response to: Alex Belo
On Wed, 6 May 2015 06:31:12 -0700, Alex Belo <b dot a dot v at inbox dot ru> wrote:

And I did not know there was a TStringList.Sort method available,
always thought one had to create the list with properties sorted and
no duplicates to make it sort.

If Sorted=true the list will be sorted on every string insertion (very
ineffective in case of big lists especially taking into account that
Delphi lists actually are arrays and insertion moves 'tail' of array on
"one position" in memory).

Do you know when the Sort method was added to TStringList?
At work we used Delphi 7 for many years and in fact I don't think they
have moved forward yet... I managed to go to BDS2006 and D2007 but
could not use that for the work projects, these were D7 only.
(I am retired from that place now and work on my own for fun).
I don't think sort was there in D7, but I may remeber wrong.

In any case I am now looking at adding a sort function to a language
file handling utility I wrote (for transferring new items from the
master to all of the different language files). This is a good way to
test it and I have it running now but with a slight problem:

All sections sort correctly, but one section contains "messages" and
they are keyed by number:

[messages]
1=some text
2=someother text
...
10=yet another text
11=and so on
100=xxx
999=yyy

The problem here is the usual sort hangup wher the result becomes:
1-10-11-...19-2-20-21-22-...29-3-30-31.. etc

I have to handle this as some kind of exception, maybe by examining
all keys in the messages section and add zeros before the numbers in
advance of sorting.
Then I have to also remove these zeros afterwards of course. Not
clean...
Something like this:
[messages]
0001=some text
0002=someother text
...
0010=yet another text
0011=and so on
0100=xxx
0999=yyy


Well, as long as I don't have to struggle through the manual sorting
in my editor....

---
Bo Berglund
Sweden & Texas
Newsreader: Forte Free Agent 1.92/32.572

Alex Belo

Posts: 626
Registered: 10/8/06
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 7, 2015 3:10 AM   in response to: Bo Berglund in response to: Bo Berglund
Bo Berglund wrote:

Do you know when the Sort method was added to TStringList?

I have BCB5 help files and this method is there:


TStringList::Sort

Sorts the strings in the list in ascending order.

virtual void __fastcall Sort(void);

Description

Call Sort to sort the strings in a list that has the Sorted property
set to false. String lists with the Sorted property set to true are
automatically sorted.

Note: Sort uses AnsiCompareStr to sort the strings. This sort order
takes into account the locale of the system on which the application is
running. To provide your own comparison operator instead, use the
CustomSort method.


TStringList::CustomSort

Description

Sorts the strings in the list in a customized order.

typedef int (CALLBACK *TStringListSortCompare)(TStringList List, int
Index1, int Index2);
virtual void __fastcall CustomSort (TStringListSortCompare Compare);

Description

Use CustomSort to sort the strings in the list, where the sort order is
defined by the Compare parameter.

Supply a value for the Compare function that compares two strings in
the string list. The List parameter provides access to the string list,
while the Index1 and Index2 parameters identify the strings to be
compared. Use Index1 and Index2 as indexes into the Strings property
array. The Compare function should return

- a value less than 0 if the string identified by Index1 comes before
the string identified by Index2
- 0 if the two strings are equivalent
- a value greater than 0 if the string with Index1 comes after the
string identified by Index2.
Do not pass NULL as the value of the Compare parameter.

Note: You must explicitly call the CustomSort method. Setting the
Sorted property only sorts strings using ANSI order, as implemented in
the Sort method.



All sections sort correctly, but one section contains "messages" and
they are keyed by number:
...
The problem here is the usual sort hangup wher the result becomes:
1-10-11-...19-2-20-21-22-...29-3-30-31.. etc

Yes, this is normal lexicographic order (quite annoying in sorting
"digital" strings). The same result will be in case of "SomeTextNNN"
where NNN is digital substring of variable length...

Time to use CustomSort ?..

For comparison in custom sort function you can try finding digital
substrings and replace them with substrings of fixed length with
leading zeros.

--
Alex
Carl-Henrik Nil...

Posts: 53
Registered: 3/26/02
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 7, 2015 4:24 AM   in response to: Bo Berglund in response to: Bo Berglund
Bo Berglund wrote:
The problem here is the usual sort hangup wher the result becomes:
1-10-11-...19-2-20-21-22-...29-3-30-31.. etc

For that problem you could try windows StrCmpLogicalW function:

//StrCmpLogicalW declaration: 
function StrCmpLogicalW(psz1, psz2: PWideChar): Integer; stdcall;
  external 'shlwapi.dll';
 
//Compare function for a TStringList CustomSort function using StrCmpLogicalW
function CompareStrings(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := StrCmpLogicalW(PWideChar(List[Index1]), PWideChar(List[Index2]));
end;
 
//Called like this:
FMyStringList.CustomSort(CompareStrings);

(Untested)
--
C-H
Bo Berglund

Posts: 757
Registered: 10/23/02
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 7, 2015 6:42 AM   in response to: Carl-Henrik Nil... in response to: Carl-Henrik Nil...
On Thu, 7 May 2015 04:24:30 -0700, Carl-Henrik Nilsson <> wrote:

Bo Berglund wrote:
The problem here is the usual sort hangup wher the result becomes:
1-10-11-...19-2-20-21-22-...29-3-30-31.. etc

For that problem you could try windows StrCmpLogicalW function:

//StrCmpLogicalW declaration: 
function StrCmpLogicalW(psz1, psz2: PWideChar): Integer; stdcall;
 external 'shlwapi.dll';
 
//Compare function for a TStringList CustomSort function using StrCmpLogicalW
function CompareStrings(List: TStringList; Index1, Index2: Integer): Integer;
begin
 Result := StrCmpLogicalW(PWideChar(List[Index1]), PWideChar(List[Index2]));
end;
 
//Called like this:
FMyStringList.CustomSort(CompareStrings);

(Untested)

Thanks Alex and Carl-Henrik for your suggestions!

Since I have a rather clearcut special case I decided to make a QD
(Quick and Dirty) modification as follows:
- When I read the file and encounter the 'Messages' section I will add
zeros at the front of the keys such that then comprise 4 digits:
0001=
0002=
...
0010=
etc
(There will never be more than 9999 separate message strings in any of
our applications using the language handler.)

Then when the complete section has been read I sort the stringlist and
then I loop through the strings and remove all leading zeros from each
string. This leaves me with a sorted stringlist with the original key
names, which can be added to the main output file stringlist:
slDest.AddStrings(slSection);

And it works fine enough for me. The Ansi versus other decoding is not
an issue here since the key names are just object property names and
these are plain Ansi everywhere.

(Sorry for the delay in replying, but when I saw the reply emails the
forum engine was down again....)

---
Bo Berglund
Sweden & Texas
Newsreader: Forte Free Agent 1.92/32.572

Rudy Velthuis (...


Posts: 7,731
Registered: 9/22/99
Re: Is there a way to sort the sections of an inifile?
Click to report abuse...   Click to reply to this thread Reply
  Posted: May 7, 2015 11:54 PM   in response to: Bo Berglund in response to: Bo Berglund
Bo Berglund wrote:

Do you know when the Sort method was added to TStringList?

AFAIK, it has always been there, from the beginning.

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

"Never go to sleep when your meat is on the fire."
-- Pueblo
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02