Solved

"pos" command with user defined starting point?

Posted on 1998-12-27
11
654 Views
Last Modified: 2010-05-18
Hi experts,

I think we all know the "pos" command.

It tells us the first occurance of a character in a string.
Unfortunately, "pos" parses a string always from the first character
to its last character.

But I need a source, where I can determine myself at which starting point
the parsing process should begin.

To clarify what I mean, I give you this example:

teststring:="this is a # test string # for the # experts';

Now I want to search for the blank character.
The "normal "pos" command would bring the result "6", because the
first blank in this string is the blank between "this" and "is".

But I need a source code who tells me the first occurence of a blank AFTER
the "#" character.

I want a routine, which in this case brings the result "11", the blank between
"#" and "test".

Have you an idea, how this could be accomplished?

With kind regards

Mathes
0
Comment
Question by:mathes
  • 4
  • 4
  • 3
11 Comments
 
LVL 27

Expert Comment

by:kretzschmar
ID: 1353246
hi mathes,

why not use

MyPos := pos('# ',teststring)+1;

meikl
0
 
LVL 20

Accepted Solution

by:
Madshi earned 30 total points
ID: 1353247
meikl,

you're right, but I guess that Mathes example is only one example. In other situations your suggestion would not work. E.g. if you want to get all the positions of the spaces in a sentence, then you should use my function "FindStr" instead.

Mathes,
with my function you can even search backwards...   :-)

function FindStr(const subStr,str: string; fromPos,toPos: cardinal) : cardinal; assembler
asm      //            EAX    EDX          ECX     [ESP+8]              EAX
        TEST    EAX,EAX                  // subStr empty ?
        JE      @@noWork
        TEST    EDX,EDX                  // str empty ?
        JE      @@fail4
        TEST    ECX,ECX                  // fromPos = 0 ?
        JE      @@fail4
        PUSH    EBX
        MOV     EBX,ECX                  // EBX = fromPos
        MOV     ECX,[ESP+12]             // ECX = toPos  (+4 w/ PUSH EBX)
        TEST    ECX,ECX                  // toPos = 0 ?
        JE      @@fail3
        PUSH    ESI
        PUSH    EDI
        MOV     ESI,EAX                  // ESI = substr
        MOV     EDI,EDX                  // EDI = str
        CMP     EBX,ECX                  // fromPos > toPos ?
        JA      @@backwards
@@forwards:
        MOV     EDX,[EDI-4]              // EDX = Length(substr)
        CMP     EBX,EDX                  // fromPos > Length(str) ?
        JA      @@fail2
        CMP     ECX,EDX                  // toPos <= Length(str) ?
        JNA     @@toPosOk
        MOV     ECX,EDX                  // toPos = Length(str)
@@toPosOk:
        MOV     EDX,[ESI-4]              // EDX = Length(substr)
        DEC     EDX                      // EDX = Length(substr) - 1
        JS      @@fail2                  // EDX < 0 ?
        PUSH    EDI                      // remember str position to calculate index
        DEC     EBX                      // dec(fromPos)
        ADD     EDI,EBX                  // "Delete (str, 1, fromPos - 1)"
        SUB     ECX,EBX                  // toPos := toPos - fromPos + 1
        SUB     ECX,EDX                  // #positions in str to look at = Length(str) - Length(substr) + 1
        JBE     @@fail1                  // #positions <= 0 ?
        MOV     AL,[ESI]                 // AL = first char of substr
        INC     ESI                      // Point ESI to 2'nd char of substr
@@fwLoop:
        REPNE   SCASB
        JNE     @@fail1
        MOV     EBX,ECX                  // save outer loop counter
        PUSH    ESI                      // save outer loop substr pointer
        PUSH    EDI                      // save outer loop str pointer
        MOV     ECX,EDX
        REPE    CMPSB
        POP     EDI                      // restore outer loop str pointer
        POP     ESI                      // restore outer loop substr pointer
        JE      @@fwFound
        MOV     ECX,EBX                  // restore outer loop counter
        JMP     @@fwLoop
@@fwFound:
        POP     EDX                      // restore pointer to first char of str
        MOV     EAX,EDI                  // EDI points of char after match
        SUB     EAX,EDX                  // the difference is the correct index
        POP     EDI
        POP     ESI
        POP     EBX
        JMP     @@noWork
@@backwards:
        MOV     EDX,[EDI-4]              // EDX = Length(substr)
        CMP     ECX,EDX                  // toPos > Length(str) ?
        JA      @@fail2
        CMP     EBX,EDX                  // fromPos <= Length(str) ?
        JNA     @@fromPosOk
        MOV     EBX,EDX                  // fromPos = Length(str)
@@fromPosOk:
        MOV     EDX,[ESI-4]              // EDX = Length(substr)
        DEC     EDX                      // EDX = Length(substr) - 1
        JS      @@fail2                  // EDX < 0 ?
        MOV     EAX,EDI                  // remember str position to calculate index
        ADD     EAX,EDX                  // add backwards calculation
        SUB     EAX,2
        PUSH    EAX
        DEC     ECX                      // dec(toPos)
        ADD     EDI,ECX                  // "Delete (str, 1, toPos - 1)"
        SUB     EBX,ECX                  // fromPos := fromPos - toPos + 1
        MOV     ECX,EBX                  // swap (fromPos, lastPos)
        ADD     EDI,ECX
        DEC     EDI
        ADD     ESI,EDX
        SUB     ECX,EDX                  // #positions in str to look at = Length(str) - Length(substr) + 1
        JBE     @@fail1                  // #positions <= 0 ?
        MOV     AL,[ESI]                 // AL = first char of substr
        DEC     ESI                      // Point ESI to 2'nd char of substr
        STD
@@bwLoop:
        REPNE   SCASB
        JNE     @@fail0
        MOV     EBX,ECX                  // save outer loop counter
        PUSH    ESI                      // save outer loop substr pointer
        PUSH    EDI                      // save outer loop str pointer
        MOV     ECX,EDX
        REPE    CMPSB
        POP     EDI                      // restore outer loop str pointer
        POP     ESI                      // restore outer loop substr pointer
        JE      @@bwFound
        MOV     ECX,EBX                  // restore outer loop counter
        JMP     @@bwLoop
@@bwFound:
        POP     EDX                      // restore pointer to first char of str + backwards calculation
        MOV     EAX,EDI                  // EDI points of char after match
        SUB     EAX,EDX                  // the difference is the correct index
        CLD
        POP     EDI
        POP     ESI
        POP     EBX
        JMP     @@noWork
@@fail0:
        CLD
@@fail1:
        POP     EDX                      // get rid of saved str pointer
@@fail2:
        POP     EDI
        POP     ESI
@@fail3:
        POP     EBX
@@fail4:
        XOR     EAX,EAX
@@noWork:
end;

Examples:
  FindStr('test','blabla test blabla test blabla',1,maxInt) -> 8
  FindStr('test','blabla test blabla test blabla',9,maxInt) -> 20
  FindStr('test','blabla test blabla test blabla',maxInt,1) -> 20
  FindStr('test','blabla test blabla test blabla',19,1) -> 8

Regards, Madshi.
0
 

Author Comment

by:mathes
ID: 1353248
Hi experts,

can you please explain to me, what this FindStr exactly does? The string to be scanned is always the same,
so why do I see different results in your examples?

      FindStr('test','blabla test blabla test blabla',1,maxInt) -> 8
      FindStr('test','blabla test blabla test blabla',9,maxInt) -> 20
      FindStr('test','blabla test blabla test blabla',maxInt,1) -> 20
      FindStr('test','blabla test blabla test blabla',19,1) -> 8


The idea of Kretzmar indeed works only in a specific situation.

What I actually want to do is this.

I want to change all occurrencies of "@teststring" to the result "@tes".


Unfortunately I don't know if the user wrote someting like

"@teststri" or "@teststr" or "@teststrin" or "@tes"

So I must be able to handle different lenghths of my master string.

Or to say it in other words

I want to change all occurencies of "@tes* " to "@tes "

There are 2 details which I know for sure:

a) The string to be scannned starts with an "@"
b) after the string to be scannend there will follow a blank.

How would you solve this problem?

With kind regards

Mathes
0
Live: Real-Time Solutions, Start Here

Receive instant 1:1 support from technology experts, using our real-time conversation and whiteboard interface. Your first 5 minutes are always free.

 
LVL 20

Expert Comment

by:Madshi
ID: 1353249
Hi Mathes,

FindStr is exactly like PosStr. But PosStr searches always from the first character to the last, while my FindStr function searches from the "fromPos" character to the "toPos" character.

Look again at these examples. The strings are the same, but the "fromPos" and "toPos" parameters change.

- FindStr('test','blabla test blabla test blabla',1,maxInt) -> 8
Looks from the first until the last character. So it returns "8", just as PosStr would do.

- FindStr('test','blabla test blabla test blabla',9,maxInt) -> 20
Begins with the search at character number 9 until the last character. So it doesn't find the "test" at position 8, but the second "test" at position 20.

- FindStr('test','blabla test blabla test blabla',maxInt,1) -> 20
Looks from behind backwards. So it returns the second "test", not the first one.

- FindStr('test','blabla test blabla test blabla',19,1) -> 8
Looks from the 19th character backwards. So it doesn't find the "test" at position 20, but the other one at position 8.

Everything clear now?

Ok, now your problem. I didn't test this code, but I guess it should work:

procedure MathesReplaceProblem(var str : string);
var i1,i2 : integer;
begin
  i1:=FindStr('@tes',str,1,maxInt);
  while i1>0 do begin
    i2:=FindStr(' ',str,i1+4,maxInt);
    if i2=0 then begin
      Delete(str,i1+4,maxInt);
      break;
    end;
    Delete(str,i1+4,i2-i1-4);
    i1:=FindStr('@tes',str,i1+4,maxInt);
  end;
end;

Regards, Madshi.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 1353250
hi mathes,

of course my comment was just a joke

a function (parameters : MasterString, Begin of the word to search, replacestring, Enddelimiter of your word to search)

function replace_it_with_delimiter(SourceS, OldS, NewS, DelS : String) : String;
var
  P,P2 : Integer;
  WordS,
  STmp, STmp2 : String;
begin
  STmp2 := '';
  STmp := SourceS;
  P := pos(OldS,STmp);
  while p <> 0 do
  begin
    STmp2 := STmp2 + copy(STmp,1,p-1)+NewS;
    delete(STmp,1,p+Length(OldS)-1);
    P2 := pos(DelS,STmp);
    delete(STmp,1,p2-1);
    P := pos(OldS,STmp);
  end;
  STmp2 := STmp2 + STmp;
  result := STmp2;
end;

call example

procedure TForm1.Button1Click(Sender: TObject);
begin
  memo1.text := replace_it_with_delimiter(memo1.text,edit1.text,edit2.text,' ');
end;

meikl
0
 

Author Comment

by:mathes
ID: 1353251
Dear Mashdi,

thank you very much for your advice. I understand now hw I can use these string routines
and your example sorce code works very well, too. And it is extremly FAST! WOW !!!

With kind regards

Mathes

0
 

Author Comment

by:mathes
ID: 1353252
Dear Mashdi,

thank you very much for your advice. I understand now hw I can use these string routines
and your example sorce code works very well, too. And it is extremly FAST! WOW !!!

With kind regards

Mathes

0
 
LVL 20

Expert Comment

by:Madshi
ID: 1353253
Mathes, yes, it's as fast as it can be (I think). I'm using this function very often in my programs, so I decided to work out a fast solution...   :-)

meikl, I didn't test your code, probably it would work, but it's quite slow, because you make Delphi reallocating the strings very often. My function does the same work much faster...

I really don't know why Inprise has not implemented a function like FindStr yet. I think it's a quite important one!

Regards, Madshi.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 1353254
hi madshi,

of course, your assemblercode is very much faster, than my little delphi-code. It can be optimized, but it will be never become the fastest solution.

Happy new year

meikl
0
 

Author Comment

by:mathes
ID: 1353255
Dear meikl,

thank you for your comment. I wanted to test your source code
but I have some problems with it.

My testprogram consists of a form with a command button,
a memo and 2 edit controls.

But now when I click on the command button
it happens nothing.


Can you please tell me what I am doing wong here?

With kind regards

Mathes


unit meikl_test;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;
 
 function replace_it_with_delimiter(SourceS, OldS, NewS, DelS : String) : String;
var
  Form1: TForm1;

implementation

function replace_it_with_delimiter(SourceS, OldS, NewS, DelS : String) : String;
      var
        P,P2 : Integer;
        WordS,
        STmp, STmp2 : String;
      begin
        STmp2 := '';
        STmp := SourceS;
        P := pos(OldS,STmp);
        while p <> 0 do
        begin
          STmp2 := STmp2 + copy(STmp,1,p-1)+NewS;
          delete(STmp,1,p+Length(OldS)-1);
          P2 := pos(DelS,STmp);
          delete(STmp,1,p2-1);
          P := pos(OldS,STmp);
        end;
        STmp2 := STmp2 + STmp;
        result := STmp2;
      end;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
 memo1.text := replace_it_with_delimiter(memo1.text,edit1.text,edit2.text,' ');
end;

end.

0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 1353256
hello mathes,

here is my unit, where i tested it.

i filled the memo with

Hello Tello Hello Hello Tello Hello Hello Tello Hello Hello Tello Hello Hello Tello Hello

edit1 with Hel
edit2 with Hallo

when i press the button each Hello becomes Hallo

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

function replace_it_with_delimiter(SourceS, OldS, NewS, DelS : String) : String;
var
  P,P2 : Integer;
  WordS,
  STmp, STmp2 : String;
begin
  STmp2 := '';
  STmp := SourceS;
  P := pos(OldS,STmp);
  while p <> 0 do
  begin
    STmp2 := STmp2 + copy(STmp,1,p-1)+NewS;
    delete(STmp,1,p+Length(OldS)-1);
    P2 := pos(DelS,STmp);
    delete(STmp,1,p2-1);
    P := pos(OldS,STmp);
  end;
  STmp2 := STmp2 + STmp;
  result := STmp2;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  memo1.text := replace_it_with_delimiter(memo1.text,edit1.text,edit2.text,' ');
end;

end.

It works also with other entrys, and it should also work in your coding. I've copied your code and it works also. No Errors. Check your inputs.

meikl


0

Featured Post

Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This Micro Tutorial demonstrates using Microsoft Excel pivot tables, how to reverse engineer competitors' marketing strategies through backlinks.
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

813 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now