Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

"pos" command with user defined starting point?

Posted on 1998-12-27
11
Medium Priority
?
659 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 120 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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
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

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Video by: ITPro.TV
In this episode Don builds upon the troubleshooting techniques by demonstrating how to properly monitor a vSphere deployment to detect problems before they occur. He begins the show using tools found within the vSphere suite as ends the show demonst…
In response to a need for security and privacy, and to continue fostering an environment members can turn to for support, solutions, and education, Experts Exchange has created anonymous question capabilities. This new feature is available to our Pr…
Suggested Courses

971 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