Mirror

Next Position of a sub-string in a string (Views: 420)


Problem/Question/Abstract:

The Pos funciton of Delphi returns the first occurence of a sub string within a string, only. How to get the positions of the next occurences?

Answer:

Solve 1:

This solution was developed using Borland Delphi 5 Service Pack 1. It is based upon the Pos algorithm delivered by Borland within the Systems unit, completely written in Assembler. !!!It might work with other versions of Borland Delphi (3.x, 4.x, 5.0) but has not been tested on them!!!

The syntax is similar to the syntax of the Pos function supplied by Delphi:

function NextPos(Substr: string; S: string; LastPos: DWORD = 0): DWORD;

NextPos returns the index value of the first character in a specified substring that occurs in a given string starting after the index value supplied by LastPos. LastPos may be omitted.

Note: As LastPos you should pass the position of the last occurence, not last position + 1. Just for convinience.

Here the commented Code:

function NextPos(SubStr: AnsiString; Str: AnsiString; LastPos: DWORD
  = 0): DWORD;
type
  StrRec = packed record
    allocSiz: Longint;
    refCnt: Longint;
    length: Longint;
  end;
const
  skew = sizeof(StrRec);

  asm
  // Search-String passed?
  TEST    EAX,EAX
  JE      @@noWork

  // Sub-String passed?
  TEST    EDX,EDX
  JE      @@stringEmpty

   // Save registers affected
PUSH ECX
PUSH EBX
PUSH ESI
PUSH EDI

// Load Sub-String pointer
MOV ESI, EAX
// Load Search-String pointer
MOV EDI, EDX
// Save Last Position in EBX
MOV EBX, ECX
// Get Search-String Length
MOV ECX, [EDI - skew].StrRec.length
// subtract Start Position
SUB ECX, EBX
// Save Start Position of Search String to return
PUSH EDI
// Adjust Start Position of Search String
ADD EDI, EBX
// Get Sub-String Length
MOV EDX, [ESI - skew].StrRec.length
// Adjust
DEC EDX
// Failed if Sub-String Length was zero
JS@@fail
// Pull first character of Sub-String for SCASB function
MOV AL, [ESI]
// Point to second character for CMPSB function
INC ESI
// Load character count to be scanned
SUB ECX, EDX
// Failed if Sub-String was equal or longer than Search-String
JLE@@fail
@@loop:
// Scan for first matching character
REPNE SCASB
// Failed, if none are matching
JNE@@fail
// Save counter
MOV EBX, ECX
PUSH ESI
PUSH EDI
// load Sub-String length
MOV ECX, EDX
// compare all bytes until one is not equal
REPE CMPSB
// restore counter
POP EDI
POP ESI
// all byte were equal, search is completed
JE@@found
// restore counter
MOV ECX, EBX
// continue search
JMP@@loop
@@fail:
// saved pointer is not needed
POP EDX
xor EAX, EAX
JMP@@exit
@@stringEmpty:
// return zero - no match
xor EAX, EAX
JMP@@noWork
@@found:
// restore pointer to start position of Search-String
POP EDX
// load position of match
MOV EAX, EDI
// difference between position and start in memory is
//   position of Sub
SUB EAX, EDX
@@exit:
// restore registers
POP EDI
POP ESI
POP EBX
POP ECX
@@noWork:
end;


Solve 2:

PosEx function:

function PosEx(SubStr: string; s: string; Index: DWord): DWord;
var
  I: Integer;
begin
  I := Pos(SubStr, Copy(s, Index, Length(s) - Index + 1));
  if I <> 0 then
    I := I + Index - 1;
  Result := I;
end;

The prarameter Index is the position you want to begin to search substr in s.

<< Back to main page