Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

TStringList...FindNext function???

174 views
Skip to first unread message

Stacey R. Brodsky

unread,
Jan 15, 2004, 11:15:43 AM1/15/04
to
Hi All,

I was wondering if anyone has any code that will allow you to find the next
occurrence of 'name' in a TStringList.

IE. MyStringList

JohnDoe=11111
JaneDoe=22222
TobyDoe=33333
JohnDoe=44444

I'd like to do the following...I am looking for JohnDoe=44444. So, I first
search MyStringList.Values['JohnDoe'] and get '11111', now, since that is
not the JohnDoe I am looking for, I would like to 'findnext' until I get
'44444' back.

I'm sure this has been done many times before, I just can't find any code in
my searches of the groups.

Any help is appreciated...Thanks In Advance,
Stacey

Ray Andrews

unread,
Jan 15, 2004, 12:17:25 PM1/15/04
to
"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message
news:4006bd2e$1...@newsgroups.borland.com...

> Hi All,
>
> I was wondering if anyone has any code that will allow you to find
the next
> occurrence of 'name' in a TStringList.
>

If you can sort the StringList, then you can just look at the next
string.


John Herbster (TeamB)

unread,
Jan 15, 2004, 12:18:48 PM1/15/04
to

"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote
> I was wondering if anyone has any code that will allow
> you to find the next occurrence of 'name' in a TStringList.

Stacey,
Why not just do a search, scanning each element of your list
for elements beginning with your name followed by '='?
Regards, JohnH

Stacey R. Brodsky

unread,
Jan 15, 2004, 1:28:28 PM1/15/04
to
Actually, while I was waiting for replies to this post, I wrote two
functions for my use with name=value in a TStringList.

They both use a temp TStringList, and save me from looping through the whole
list to find a particular entry. Here they are if anyone is interested. I
wrote them both very quickly, so I'm sure they can be a bit cleaner...but,
they work.

// Returns the number of times sName occurs in the Name portion of the
stringlist

function SLGetNameCount(sList : TStrings; sName : string) : Integer;
var
TempList : TStringList;
Idx : Integer;

begin
try
Result := 0;
TempList := TStringList.Create;
TempList.Assign(sList);

while Idx <> -1 do begin
Idx := TempList.IndexOfName(sName);
if Idx <> -1 then begin
Inc(Result);
TempList.Delete(Idx);
end;
end;
finally
FreeAndNil(TempList);
end;
end;

// Returns the index of a duplicate sName entry by validating the existence
of sPValue in the Value portion of the string. Returns -1 if no matches are
found.

function SLGetNameIdxByPartialValue(sList : TStrings; sName, sPValue :
string; iPValueStartPos : Integer) : Integer;
var
TempList : TStringList;
Idx : Integer;
LinesDeleted : Integer;
Found : Boolean;

begin
try
Result := -1;
LinesDeleted := 0;
TempList := TStringList.Create;
TempList.Assign(SL);

while Idx <> -1 do begin
Idx := TempList.IndexOfName(sName);
if Idx <> -1 then begin
if iPValueStartPos >= 0 then
Found := Copy(TempList.Values[sName], iPValueStartPos,
Length(sPValue)) = sPValue
else
Found := Pos(sPValue, TempList.Values[sName]) > 0;

if Found then begin
Result := Idx+LinesDeleted;
Exit;
end
else begin
TempList.Delete(Idx);
Inc(LinesDeleted);
end;
end;
end;
finally
FreeAndNil(TempList);
end;
end;


"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message
news:4006bd2e$1...@newsgroups.borland.com...

Dave Keighan

unread,
Jan 15, 2004, 2:20:12 PM1/15/04
to
Stacey

> I was wondering if anyone has any code that will allow you to find
> the next occurrence of 'name' in a TStringList.

If you know the entire string won't
StringList.IndexOf('JohnDoe=44444');
return what you're looking for or are you specifically looping through
all the JohnDoe(s)?

--
Dave Keighan
Posted using XanaNews 1.15.8.5

Stacey R. Brodsky

unread,
Jan 15, 2004, 3:48:23 PM1/15/04
to
Hmm, if only it were that simple...actually, it is a list of our members,
and the '44444' is an id number from a previous system. Also, the '44444' is
preceded by a bunch of other characters which I lay into a record when I
find the correct string. So, in theory, there could be 7 JohnDoe's, but I
have to find the one with '44444' at a particular spot in the 'value'
string.

But, thanks for your input =)
"Dave Keighan" <SpamTrap...@yahoo.com> wrote in message
news:4006f5fc$1...@newsgroups.borland.com...

Ian Stuart

unread,
Jan 15, 2004, 3:52:18 PM1/15/04
to
"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message
news:4006dc4b$1...@newsgroups.borland.com...

> Actually, while I was waiting for replies to this post, I wrote two
> functions for my use with name=value in a TStringList.

I am perhaps missing something here in your requirements but there are 101
ways to do this.

To get 'JohnDoe=44444' you don't need to search on the name part - use all
of the search pattern.

// if s = 'JohnDoe' and v = '44444' and sList is an unsorted case-sensitive
TStringList then

idx := sList.IndexOf(s + '=' + v);


To find duplicate name entries you would use less code and less memory by
iterating the list yourself...especially if the
stringlist is not case sensitive.

// sname = 'JohnDoe';
var
I: Integer;
V: string;
dupeNames: Integer;
begin
dupeNames := 0;
sname := sname + '=';
for I := 0 to sList.Count-1 do
begin
V := sList[I];
if AnsiStartsStr(s, V) then // or AnsiStartsText, or AnsiStrLComp etc..
begin
Inc(dupeNames);
Delete(V, 1, Length(sname));
// V is now the value part


end;
end;
end;

If you need to implement a findnext function then you can iterate from a
start position...

//Pass 0 or another start value for index
//Returns true if found and adjusts index to the next value to use.

function StringsFindNext(sList: TStrings; var index: Integer;
const AName: string; var Value: string; const caseSensitive: Boolean =
true): Boolean;
var
namePart: string;
begin
Result := false;
if index >= 0 then
begin
namePart := AName + '=';
while index < sList.Count do
begin
Value := sList[index];
Result := (caseSensitive and AnsiStartsStr(namePart, Value))
or (not caseSensitive and AnsiStartsText(namePart, Value));
if Result then
begin
Delete(Value, 1, Length(namePart));
Break;
end;
Inc(index);
end;
end;
end;

procedure myProcWithStrings(const myName: string);
var
idx: Integer;
someValue: string;
begin
idx := -1;
// here, the first loop entry will have index = 0!
while StringsFindNext(someStringsReference, idx+1, myName, someValue) do
begin
// idx is the index of the line having the name myName
// someValue is the value portion of the line..
// Do something....
end;
end;


I enjoyed typing this, much better than working :-)

Disclaimer:
Code produced by I.S. is unreliable, unlikely to work and not recommended
for professional use or farm animals. Examples, when provided, usually do
not work the first time, the second time or any time therafter.

Regards
Ian Stuart


Pieter Zijlstra

unread,
Jan 15, 2004, 3:47:44 PM1/15/04
to
Stacey R. Brodsky wrote...

> Actually, while I was waiting for replies to this post, I wrote two
> functions for my use with name=value in a TStringList.
>
> They both use a temp TStringList, and save me from looping through
> the whole list to find a particular entry. Here they are if anyone is
> interested. I wrote them both very quickly, so I'm sure they can be a
> bit cleaner...but, they work.

For now they work, but they might not work in the future...

Turn on Hint and Warnings! See Projects->Options tab Compiler.
(and never turn them off again <g>)

1) Variable Idx 'might' not have been initialized.
Idx is located on the stack and might have any value, whatever happens
to be on the stack at that moment, also -1.
So before entering the while loop initialize Idx (zero for instance).

2) The way you construct something within a try..finally..end is
dangerous. Should be...
TempList := TStringList.Create;
try
// do the things you want to do with TempList
finally
// FreeAndNil(TempList);
TempList.Free;
end;
In your case if anyhything goes wrong with the creation of the
stringlist, TempList will not be initialized and blow up when
FreeAndNill tries to Free the non-existing object (AV's).
BTW FreeAndNill isn't really necessary here since TempList is no longer
used after the function. Changing that to a normal TempList.Free would
also show another warning that TempList might not have been initialized!

--
Pieter

Ray Andrews

unread,
Jan 15, 2004, 4:16:59 PM1/15/04
to

"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message
news:4006...@newsgroups.borland.com...

> Hmm, if only it were that simple...actually, it is a list of our
members,
> and the '44444' is an id number from a previous system. Also, the
'44444' is
> preceded by a bunch of other characters which I lay into a record
when I
> find the correct string. So, in theory, there could be 7 JohnDoe's,
but I
> have to find the one with '44444' at a particular spot in the
'value'
> string.

How about this ?

* UNTESTED *

function TForm1.FindJohnDoe(doe,id: String; idpos: Integer; src:
TStrings): String;
var
newlist: TStringList;
doepos: Integer;
doeval: String;
begin
result := '';
if (doe = '') or (id = '') or (idpos < 1) or (src.Count < 1) then
Exit;
newlist := TStringList.Create;
newlist.Text := src.Text;
doepos := newlist.IndexOfName(doe);
while (doepos >= 0) and (result = '') do
begin
doeval := newlist.Values[doe];
if (Copy(doeval,idpos,Length(id)) = id) then
result := doeval;
newlist.Delete(doepos);
doepos := newlist.IndexOfName(doe)
end;
newlist.Free;
end;


Dave Keighan

unread,
Jan 15, 2004, 3:12:10 PM1/15/04
to
Stacey R. Brodsky wrote:

> I have to find the one with '44444' at a particular spot
> in the 'value' string.

Ahhh, I see. Was worth a whack though.


> But, thanks for your input =)

Your very welcome thanks for taking the time to explain the details.

Glynn Owen

unread,
Jan 15, 2004, 4:21:54 PM1/15/04
to
"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message
news:4006bd2e$1...@newsgroups.borland.com...

If nobody else has mentioned it, you might look at the help system for
TStringList about the property "Names".

HTH, Glynn


jacob muntner

unread,
Jan 16, 2004, 6:42:52 AM1/16/04
to

‏‏"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> כתב בהודעה
news:4006bd2e$1...@newsgroups.borland.com...

> Hi All,
>
> I was wondering if anyone has any code that will allow you to find the
next
> occurrence of 'name' in a TStringList.

as mentioned, a sorted list would make it easier, because
all occurrences of 'name' will be adjacent. so if
idx := aList.IndexofName(MyName); // 1st occurrence
if (idx<>-1) and (aList.Names[idx+1] = MyName)
then...... // 2nd occurrence
-------
jacob


Bruce Roberts

unread,
Jan 16, 2004, 6:50:32 PM1/16/04
to

"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message
news:4006dc4b$1...@newsgroups.borland.com...

> Actually, while I was waiting for replies to this post, I wrote two
> functions for my use with name=value in a TStringList.
>
> They both use a temp TStringList, and save me from looping through the
whole
> list to find a particular entry. Here they are if anyone is interested. I
> wrote them both very quickly, so I'm sure they can be a bit cleaner...but,
> they work.

An alternative that doesn't require counting the entries first

function SLGetNextEntry (sList : tStrings; sName : string; last : integer) :
integer;

var lth : integer;

begin
inc (last);
sName := sName + '=';
lth := Length (sName);
while (last < sList.Count) and (ANSICompareText (Copy (sList [last], 1,
lth), sName) <> 0) do
inc (last);
if last >= sList.Count
then result := -1
else result := last;
end;

For any given sName, Last should initially be -1 and subsequent calls should
be the value returned on the previous call for sName. The function
returns -1 if there are no entries following Last that (partially) match
sName.

If the order in which the entries are returned is immaterial, then the above
can be made more efficient by working backwards through the list, in which
case the initial value for Last should be sList.Count.

function SLGetNextEntry (sList : tStrings; sName : string; last : integer)
:;

var lth : integer;

begin
result := Pred (last);
sName := sName + '=';
lth := Length (sName);
while (result >= 0) and (ANSICompareText (Copy (sList [result], 1, lth),
sName) <> 0) do
dec (result);
end;


Stacey R. Brodsky

unread,
Jan 19, 2004, 11:10:34 AM1/19/04
to
Thanks to all of you! Your answers/comments have been very helpful. My
appreciation to all.


"Stacey R. Brodsky" <sbrodsky68@^nospam^aol.com> wrote in message

news:4006bd2e$1...@newsgroups.borland.com...

0 new messages