Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Alternative zu PosEx (https://www.delphipraxis.net/216199-alternative-zu-posex.html)

Amateurprofi 19. Nov 2024 12:50

Alternative zu PosEx
 
Ich hatte das Problem, zu prüfen, ob und wo in einem bestimmten Bereich eines etwas längeren Strings (SearchIn) ein anderer Text (SearchFor) vorkommt.
Ein weiteres Problem war, alle Fundstellen von SearchFor in SearchIn zu finden und zu speichern.
Für das erste Problem habe ich die Funktion "StrPos" geschrieben, für das zweite die Funktion "StrPosEx".
Beide Funktionen sind für 32bit-Assembler geschrieben.
Alternative 64bit Versionen werde ich demnächst erstellen.
Zum Testen habe ich die kürzlich gefundene 52te Mersenne-Primzahl mit 41.024.320 Ziffern verwendet.
Zum Finden und Speichern der 4.103.481 "0"en benötigt die Funktion ca. 65 ms.
Für die 36 Fundstellen der Ziffernfolge "030366" (Geburtstag einer Freundin) zu finden und zu speichern, werden ca. 60 ms benötigt.
(Windows 7 - Intel Core I7-2600K 3.4 GHz).
Um mich nicht zu sehr mit fremden Federn zu schmücken:
Die Funktionen sind von der Funktion "PosEx" aus System.StrUtils.pas abgeleitet.
Vielleicht hat der eine oder andere Bedarf für solche Funktionen.

Delphi-Quellcode:
{$IFDEF CPUX86}
{------------------------------------------------------------------------------}
{ StrPos                                                                      }
{ Prüft, ob SearchFor in SearchIn innerhalb des Bereiches First..Last         }
{ vorkommt und gibt die gefundene Position. bzw 0 für 'nicht gefunden' zurück. }
{ First<1 heißt ab Anfang von SearchIn suchen.                                }
{ Last<1 oder Last>Length(SearchIn) heißt bis Ende von SearchIn suchen.       }
{------------------------------------------------------------------------------}
FUNCTION StrPos(const SearchFor,SearchIn:String; First,Last:Integer):Integer;
asm
                  // EAX=@SearchFor, EDX=@SearchIn, ECX=First, Stack=Last
               test eax,eax
               jz   @Nil             // SearchFor leer
               test edx,edx
               jz   @Nil             // SearchIn leer
               dec  ecx              // First 0-Based
               jge  @FirstOk         // First>=1
               xor  ecx,ecx
@FirstOK:     push ebx
               push edi
               push esi
               mov  ebp,Last
               mov  ebx,[edx-4]      // Length(SearchIn)
               test ebp,ebp
               jle  @LastOK          // Last<=0 heißt bis Ende suchen
               cmp  ebp,ebx
               jge  @LastOK          // Last>Length(SearchIn), bis Ende suchen
               mov  ebx,ebp          // Nur bis Last suchen
@LastOK:      // EAX=@SearchFor, EDX=@SearchIn, ECX=First-1, EBX=Last
               mov  edi,[eax-4]      // Length(SearchFor)
               lea  esi,[ecx+edi]    // First+Length(SearchFor)-1
               cmp  esi,ebx
               jg   @Past            // First>Last oder First+Length(SearchFor)>Last
               lea  eax,[eax+edi*2-2] // @SearchFor[Length(SearchFor)]
               lea  ebp,[edx+ebx*2]  // @SearchIn[Last+1]
               lea  edx,[edx+esi*2-2] // @SearchIn[First+Length(SearchFor)-1]
               lea  edi,[edi*2-2]    // 2*(Length(SearchFor)-1)
               neg  edi              // -(2*(Length(SearchFor)-1))
               add  ecx,ecx          // 2*(First-1)
               sub  ecx,edx          // 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               push ecx              // Für spätere Positionsberechnung
               movzx ecx,word[eax]    // letztes Zeichen von SearchFor
               // --------------------------------------------------------------
               // CX = Letztes Zeichen von SearchFor
               // EDX = SearchIn[First+Length(SearchFor)
               // EBP = @SearchIn[Last+1 Zeichen]
               // EDI = -(2*(Length(SearchFor)-1))
               // ESI = First+Length(SearchFor)-1
@Loop:        cmp  cx,[edx]         // Letzes Zeichen von SearchFor an EDX?
               jz   @Test0            // Ja, SearchIn auf voriges Zeichen
@AfterTest0:  cmp  cx,[edx+2]       // Letzes Zeichen von SearchFor an EDX+1 Zeichen
               jz   @TestT           // J,
@AfterTestT:  add  edx,8             // SearchIn+4 Zeichen
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  edx,-4            // SearchIn-2 Zeichen
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @False           // Nicht gefunden
@Continue:    cmp  cx,[edx-4]       // Letzes Zeichen von SearchFor an EDX-2 Zeichen?
               jz   @Test2            // Ja, SearchIn -3 Zeichen
               cmp  cx,[edx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  edx,2             // SearchIn + 1 Zeichen, durch folgende Adds - 2 Zeichen
@Test2:       add  edx,-4            // SearchIn - 2 Zeichen, durch folgendes Add - 3 Zeichen
@Test0:       add  edx,-2            // SearchIn - 1 Zeichen
@TestT:       mov  esi,edi
               test esi,esi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
@String:      mov  ebx,[eax+esi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               cmp  esi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[eax+esi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               add  esi,8             // Zeichenzahl + 4 Zeichen
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
@Found:       lea  eax,[edx+4]      // Fundstelle
               cmp  eax,ebp          // Im zu durchsuchenden Bereich?
               ja   @False           // Nein, nicht gefunden.
               add  eax,[esp]        // Endgültige Position in Bytes
               shr  eax,1             // Endgültige Position in Zeichen
               jmp  @End             // Stack bereinigen, Register wieder herstellen
               //---------------------------------------------------------------
@False:       xor  eax,eax          // Nicht gefunden
               jmp  @End             // Stack bereinigen, Register wieder herstellen
               //---------------------------------------------------------------
@Nil:         xor  eax,eax          // Nicht gefunden
               jmp  @Ret             // Return
               //---------------------------------------------------------------
@Past:        xor  eax,eax          // Nicht gefunden
               jmp  @Pop             // Register wieder herstellen
               //---------------------------------------------------------------
@End:         add  esp,4             // Stack bereinigen
@Pop:         pop  esi              // ESI wieder herstellen
               pop  edi              // EDI wieder herstellen
               pop  ebx              // EBX wieder herstellen
@Ret:
end;
{$ENDIF}
Delphi-Quellcode:
{$IFDEF CPUX86}
{------------------------------------------------------------------------------}
{ StrPosEx                                                                    }
{ Sucht alle Vorkommen von SearchFor in SearchIn und stellt die Positionen    }
{ der Fundstellen in Positions.                                               }
{ Es ist Sache der aufrufenden Stelle, sicherzustellen, daas Positions lang   }
{ genug ist, alle Fundstellen zu speichern.                                   }
{ Parameter                                                                   }
{    SearchFor : String, nach dem gesucht wird.                              }
{    SearchIn  : String, inem gesucht wird.                                  }
{    Positions : Array zur Speicherung der Fundstellen.                      }
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                   Bit 0 = 1 : Wenn SearchFor leer ist.                      }
{                   Bit 1 = 1 : Wenn SearchIn leer ist.                       }
{                   Bit 2 = 1 : Wenn SearchFor länger ist, als SearchIn.      }
{                   Bit 3 = 1 : Wenn Positions zu kurz ist.                   }
{                 Wenn ein Fehler auftritt, und das korrespondierende Bit in  }
{                 Exceptions nicht gesetzte ist, werden folgende Fehlercodes  }
{                 zurückgegeben:                                              }
{                    -1 SearchFor leer.                                       }
{                    -2 SearchIn leer.                                        }
{                    -3 SearchFor länger als SearchIn.                        }
{                    -4 Positions zu kurz.                                    }
{ Wenn kein Fehler auftritt, wird die Anzahl der Fundstellen zurückgegeben.   }
{------------------------------------------------------------------------------}
FUNCTION StrPosEx(const SearchFor,SearchIn:String;
   Positions:TIntegerDynArray; Exceptions:Integer=0):Integer;
const
   sSearchForEmpty:String='StrPosEx:'#13'SearchFor ist leer';
   sSearchInEmpty:String='StrPosEx:'#13'SearchIn ist leer';
   sSearchForTooLong:String='StrPosEx:'#13'Searchfo ist länger als SearchIn';
   sPositionsLength:String='StrPosEx:'#13'Das Array "Positions" ist zu kurz '+
                           'um alle Fundstellen zu speichern';
   pExClass:ExceptClass=(Exception);
asm
               // EAX=@SearchFor, EDX=@SearchIn, ECX=Positions
               // Register retten und Platz für lokale Variablen reservieren
               push ebp
               push ebx
               push edi
               push esi
               sub  esp,12            // Platz für 3 Integers
               // Prüfen, ob SearchFor und SearchIn nicht leer sind
               test eax,eax          // SearchFor leer?
               jz   @SFNil           // Ja, Fehlermedung
               test edx,edx          // SearchIn leer?
               jz   @SINil           // Ja, Fehlermeldung
               // Längen von SearchFor und SearchIn laden und prüfen ob
               // SearchFor nicht länger ist, als SearchIn
               mov  edi,[eax-4]      // Length(SearchFor)
               mov  ebx,[edx-4]      // Length(SearchIn)
               cmp  edi,ebx          // SearchFor länger als SearchIn?
               ja   @SFTooLong       // Ja, Fehlermeldung
               cmp  edi,1             // Hat SearchFor nur 1 Zeichen
               je   @Char            // Ja
               // Positions retten, Anzahl Fundstellen auf 0
               mov  [esp+8],ecx      // Positions
               mov  [esp+4],0         // Anzahl Fundstellen
               // Zeiger und Länge von SearchIn initialsieren
               lea  eax,[eax+edi*2-2] // EAX auf letztes Zeichen in SearchFor
               lea  ebp,[edx+ebx*2]  // EBP hinter letztes Zeichen von SearchIn
               lea  edx,[edx+edi*2-2] // EDX auf Ende der ersten potentiellen Fundstelle
               lea  edi,[edi*2-2]    // EDI = 2*(Length(SearchFor)-1)
               neg  edi              // EDI = -(2*(Length(SearchFor)-1))
               xor  ecx,ecx
               sub  ecx,edx          // ECX = -SearchIn[Length(SearchFor)-1]
               mov  [esp],ecx        // Für spätere Positionsberechnung
               // --------------------------------------------------------------
               // EAX    = Zeigt auf letztes Zeichen in SearchFor
               // EDX    = Zeigt auf Ende der nächsten potentiellen Fundstelle
               // EBP    = Zeigt hinter letztes Zeichen von SearchIn
               // EDI    = -(2*(Length(SearchFor)-1))
               // [ESP-8] = Postions
               // [ESP-4] = Anzahl Fundstellen
               // [ESP]  = -SearchIn[Length(SearchFor)-1]
@Start:       mov  cx,[eax]         // letztes Zeichen von SearchFor
@Loop:        cmp  cx,[edx]         // CX an [EDX]?
               jz   @Test0            // Ja, weitere Zeichen ab [EDX-1 Char] prüfen
@AfterTest0:  cmp  cx,[edx+2]       // CX an [EDX + 1 Char]?
               jz   @TestT           // Ja, weitere Zeichen ab [EDX] prüfen
@AfterTestT:  add  edx,8             // SearchIn + 4 Chars
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  edx,-4            // SearchIn - 2 Chars
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NoFurther       // Keine weiteren Fundstellen
@Continue:    cmp  cx,[edx-4]       // CX an [EDX - 2 Chars]?
               jz   @Test2            // Ja, SearchIn - 3 Chars
               cmp  cx,[edx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  edx,2             // SearchIn + 1 Char, durch folgende Adds - 2 Chars
@Test2:       add  edx,-4            // SearchIn - 2 Chars, durch folgendes Add - 3 Chars
@Test0:       add  edx,-2            // SearchIn - 1 Char
@TestT:       mov  esi,edi          // -(2*(Length(SearchFor)-1))
               test esi,esi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
@String:      mov  ebx,[eax+esi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               cmp  esi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[eax+esi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               add  esi,8             // Zeichenzahl + 4 Chars
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
               // Gefunden. EDX zeigt auf Fundstelle - 1 Zeichen
@Found:       lea  ecx,[edx+4]      // Fundstelle + 1 Zeichen
               cmp  ecx,ebp          // Im zu durchsuchenden Bereich?
               ja   @NoFurther       // Nein, keine weiteren Fundstellen
               add  ecx,[esp]        // Position in Bytes
               shr  ecx,1             // Position in Zeichen
               mov  esi,[esp+8]      // Positions
               mov  ebx,[esp+4]      // Bisherige Anzahl Fundstellen
               cmp  ebx,[esi-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               mov  [esi+ebx*4],ecx  // Fundstelle speichern
               inc  ebx              // Anzahl Fundstellen + 1
               mov  [esp+4],ebx      // Anzahl Fundstellen speichern
               // EDX auf nächste potentielle Fundstelle
               mov  esi,edi          // -(2*(Length(SearchFor)-1))
               neg  esi              //   2*(Length(SearchFor)-1)
               lea  edx,[edx+esi+4]  // EDX=nächste potentielle Fundstelle
               cmp  edx,ebp          // Noch im gültigen Bereich?
               jb   @Start           // Ja, weiter suchen
               jmp  @NoFurther       // Nein, keine weiteren Fundstellen
               // --------------------------------------------------------------
               // Suche nach nur einem Zeichen
               // EAX = SearchFor
               // EDX = SearchIn
               // ECX = Positions
               // EBX = Lenght(SearchIn)
@Char:        mov  ebp,ecx          // Positions
               xor  ecx,ecx          // Anzahl Fundstellen
               lea  edx,[edx+ebx*2]  // Hinter letztes Zeichen von SearchIn
               lea  edi,[ebx+1]      // für spötere Positionsermittlung
               neg  ebx
               mov  ax,[eax]         // gesuchtes Zeichen
@CharLoop:    cmp  ax,[edx+ebx*2]   // AX an aktueller Position?
               jz   @CharFound1       // Ja
               cmp  ax,[edx+ebx*2+2] // AX an nächster Position?
               jz   @CharFound2       // Ja
               add  ebx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               // An [EDX+EBX*2] gefunden
@CharFound1:  cmp  ecx,[ebp-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  esi,[edi+ebx]    // Fundstelle
               mov  [ebp+ecx*4],esi  // In Positions speichern
               inc  ecx              // Anzahl Fundstellen
               inc  ebx              // 1 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               // An [EDX+EBX*2+2] gefunden
@CharFound2:  cmp  ecx,[ebp-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  esi,[edi+ebx+1]  // Fundstelle * 2
               mov  [ebp+ecx*4],esi  // In Positions speichern
               inc  ecx              // Anzahl Fundstellen
               add  ebx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               //---------------------------------------------------------------
               // SearchFor ist leer
@SFNil:       mov  eax,sSearchForEmpty
               mov  ecx,1             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchIn ist leer
@SINil:       mov  eax,sSearchInEmpty
               mov  ecx,2             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchFor ist länger als SearchIn
@SFTooLong:   mov  eax,sSearchForTooLong
               mov  ecx,3             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions nicht lang genug um neue Fundstelle zu speichern
@OutOfMem:    mov  eax,sPositionsLength
               mov  ecx,4             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Keine weiteren Fundstellen
@NoFurther:   mov  ecx,[esp+4]      // Anzahl Fundstellen
@NoFurtherC:  xor  eax,eax          // ´Kein Fehler
               //---------------------------------------------------------------
               // Stack bereinigen und Register restaurieren
@End:         add  esp,12            // Stack bereinigen
               pop  esi              // ESI wieder herstellen
               pop  edi              // EDI wieder herstellen
               pop  ebx              // EBX wieder herstellen
               pop  ebp              // EBP wieder herstellen
               //---------------------------------------------------------------
               // Prüfen ob Fehler vorliegt und ggfs. Exception auslösen
               test eax,eax          // Fehler ?
               jz   @NoError         // Nein, Anzahl Fundstellen zurückgeben
               mov  edx,1
               shl  edx,cl
               neg  ecx              // Fehlercode
               shr  edx,1
               test [ebp+8],edx      // Exception werfen?
               jz   @NoError         // Nein, nur Fehlercode zurückgeben
               mov  ecx,eax          // Fehlertext
               mov  eax,pExClass     // InstanceOrVMT
               mov  edx,1             // Alloc
               call Exception.Create
               call System.@RaiseExcept
               //---------------------------------------------------------------
               // Anzahl Fundstellen bzw. Fehlercode in Result
@NoError:     mov  eax,ecx          // Anzahl Fundstellen bzw. FehlerCode
end;
{$ENDIF}

himitsu 19. Nov 2024 13:55

AW: Alternative zu PosEx
 
Lies besser nicht meine Signatur. :stupid:

OK, bei dem alten Delphi solltest du das überall das "Pos" in "PosEx" umbenennen .... ist aber eigentlich sieit vielen Jahren nicht mehr nötig. :angle:
Delphi-Quellcode:
function Pos(const SubStr, Str: String; Offset, LastOffset: Integer): Integer; overload;
begin
  //if Offset > 0 then
  //  Result := Pos(SubStr, Str, Offset)
  //else
  //  Result := Pos(SubStr, Str);
  Result := Pos(SubStr, Str, Offset);
  if Result > LastOffset then
    Result := 0;  
end;
:duck:

Sinspin 19. Nov 2024 15:05

AW: Alternative zu PosEx
 
Zitat:

Zitat von Amateurprofi (Beitrag 1543240)
Delphi-Quellcode:
{$IFDEF CPUX86}
...

Das ist aber nur für 32 Bit?
Wie sieht es denn mit einer 64Bit Variante aus?
Ich arbeite in letzter Zeit häufiger mit Lazarus FPC und 64Bit um ein bisschen mehr Speicher verballern zu können.
Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre.

Nebenbei, POS, auch mal als Point Of Sale kennengelernt, hat mir mal eine Verwarnung von Apple eingebracht als ich eine App bewertet habe.
Die haben wohl was anderes verstanden:lol: ... was ich leider auch so angedacht hatte.

Gausi 19. Nov 2024 16:04

AW: Alternative zu PosEx
 
Zitat:

Zitat von Sinspin (Beitrag 1543246)
Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre.

Wenn die Strings größer werden, und man oft (oder viele) Teilstrings in vielen verschiedenen Texten suchen will, dann wird das schon interessant. Aber dann nimmt man ggf. auch andere Suchalgorithmen. Knuth-Morris-Pratt, Boyer-Moore, QuickSearch oder ein paar weitere, die ggf. auf bestimmte Spezialfälle hin optimiert sind. Parameter dafür sind u.a.
  • Länge der gesuchten Teilstrings
  • Größe des verwendeten Alphabets (also Anzahl der verschiedenen Zeichen)
Pos und PosEx laufen dabei unter "naive Verfahren".

Ein Anwendungsfall ist z.B. das Durchsuchen eines Genoms nach einer bestimmten DNA-Sequenz. Zumindest war das öfter ein Beispiel in der Literatur, als ich damals meine Diplomarbeit über "Algorithmen zur Mustersuche in Zeichenketten" geschrieben habe. ;-)

Aber es stimmt schon, dass das meistens irrelevant ist - denn selbst der "naive Algorithmus" hat eine (erwartete) lineare Laufzeit. KMP ist interessant, weil da auch die Worst-Case-Laufzeit linear ist (im Gegensatz zu quadratisch bzw. "rechteckig", d.h. Textlänge*Musterlänge im Worst Case bei Pos). Boyer-Moore z.B. wird schneller, wenn die Suchmuster länger werden.

Oder halt komplette andere Ansätze, wie z.B. vorher einen Suchindex über die zu durchsuchenden Texte aufbauen, aber das ist dann wirklich was komplett anderes.

himitsu 19. Nov 2024 16:22

AW: Alternative zu PosEx
 
War Pos nicht früher (vor Äonen) mal langsamer?
Ich glaub dort wurde auch was vom FastStrings mal mit übernommen.

Amateurprofi 20. Nov 2024 00:39

AW: Alternative zu PosEx
 
Zitat:

Zitat von Sinspin (Beitrag 1543246)
Zitat:

Zitat von Amateurprofi (Beitrag 1543240)
Delphi-Quellcode:
{$IFDEF CPUX86}
...

Das ist aber nur für 32 Bit?
Wie sieht es denn mit einer 64Bit Variante aus?
Ich arbeite in letzter Zeit häufiger mit Lazarus FPC und 64Bit um ein bisschen mehr Speicher verballern zu können.
Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre.

Nebenbei, POS, auch mal als Point Of Sale kennengelernt, hat mir mal eine Verwarnung von Apple eingebracht als ich eine App bewertet habe.
Die haben wohl was anderes verstanden:lol: ... was ich leider auch so angedacht hatte.

Zu "Das ist aber nur für 32 Bit?" und "Wie sieht es denn mit einer 64Bit Variante aus?"
Aus #1
"Beide Funktionen sind für 32bit-Assembler geschrieben.
Alternative 64bit Versionen werde ich demnächst erstellen".

Zu "Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre."
Nicht verzagen, kommt irgendwann.

Bei mir geht es um Mersenne-Primzahlen und aus diesen abgeleiteten Perfekten Zahlen, die, Stand 10/2024, bis zu ca. 41 Mio bzw. 82 Mio Ziffern haben.
Das Programm in dem die Funktionen benutze, kann diese Zahlen errechnen, speichern, laden, in einem Listenfeld anzeigen, und in ihnen Ziffern oder Ziffernfolgen suchen.
Der Suchtext wird in ein TEdit eingegeben und bei jeder Veränderung des Eingabefeldes werden alle Fundstellen ermittelt und im Listenfeld farbig markiert.
Bei der Eingabe einer Ziffernfolge wird die Funktion also nach jedem Tastendruck ausgeführt, und ich möchte eine Ziffernfolge zügig eingeben können, und keine unschönen Wartezeiten hinnehmen.
Na klar habe ich früher die von himitsu skizzierte Lösung verwendet, aber gemerkt, dass dabei die erwähnten "unschönen Wartezeiten" auftraten.

Nochmal zu "Wie sieht es denn mit einer 64Bit Variante aus?"
Warum fragst du dass wenn du sagst "Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre."

Amateurprofi 20. Nov 2024 00:47

AW: Alternative zu PosEx
 
Zitat:

Zitat von himitsu (Beitrag 1543250)
War Pos nicht früher (vor Äonen) mal langsamer?
Ich glaub dort wurde auch was vom FastStrings mal mit übernommen.

Ja. Das lag daran, dass in der "alten", bei mir (Delphi XE2) "aktuellen" Version mit dem bummeligen "REPNE SCASB" gearbeitet wird.

Amateurprofi 20. Nov 2024 00:51

AW: Alternative zu PosEx
 
Zitat:

Zitat von Gausi (Beitrag 1543249)
Zitat:

Zitat von Sinspin (Beitrag 1543246)
Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre.

Wenn die Strings größer werden, und man oft (oder viele) Teilstrings in vielen verschiedenen Texten suchen will, dann wird das schon interessant. Aber dann nimmt man ggf. auch andere Suchalgorithmen. Knuth-Morris-Pratt, Boyer-Moore, QuickSearch oder ein paar weitere, die ggf. auf bestimmte Spezialfälle hin optimiert sind. Parameter dafür sind u.a.
  • Länge der gesuchten Teilstrings
  • Größe des verwendeten Alphabets (also Anzahl der verschiedenen Zeichen)
Pos und PosEx laufen dabei unter "naive Verfahren".

Ein Anwendungsfall ist z.B. das Durchsuchen eines Genoms nach einer bestimmten DNA-Sequenz. Zumindest war das öfter ein Beispiel in der Literatur, als ich damals meine Diplomarbeit über "Algorithmen zur Mustersuche in Zeichenketten" geschrieben habe. ;-)

Aber es stimmt schon, dass das meistens irrelevant ist - denn selbst der "naive Algorithmus" hat eine (erwartete) lineare Laufzeit. KMP ist interessant, weil da auch die Worst-Case-Laufzeit linear ist (im Gegensatz zu quadratisch bzw. "rechteckig", d.h. Textlänge*Musterlänge im Worst Case bei Pos). Boyer-Moore z.B. wird schneller, wenn die Suchmuster länger werden.

Oder halt komplette andere Ansätze, wie z.B. vorher einen Suchindex über die zu durchsuchenden Texte aufbauen, aber das ist dann wirklich was komplett anderes.

Ich glaube, weiß es aber nicht, wenn es darum geht, alle Fundstellen (in aufsteigender Reihenfolge) zu finden, wird man um eine lineare Suche nicht herumkommen.

himitsu 20. Nov 2024 02:54

AW: Alternative zu PosEx
 
Da gab es auf der EKON einen Vortrag, wo eine 1 Billionen-Zeilen-CSV binnen kürzester Zeit gelesen/verarbeitet wird. (war im anderen Raum, wo ich nicht drin war :duck:)

Gausi 20. Nov 2024 05:25

AW: Alternative zu PosEx
 
Zitat:

Zitat von Amateurprofi (Beitrag 1543278)
Ich glaube, weiß es aber nicht, wenn es darum geht, alle Fundstellen (in aufsteigender Reihenfolge) zu finden, wird man um eine lineare Suche nicht herumkommen.

Wenn du ein einzelnes Zeichen suchst (z.B. die "0"), dann stimmt das. Wenn du aber z.B. alle Vorkommen der ersten 10 Stellen von Pi suchen willst (um mal bei Zahlen zu bleiben), dann muss man nicht unbedingt jedes einzelne Zeichen im Suchtext angucken und mit einem Zeichen im Muster vergleichen. So auf Anhieb würde ich wetten, dass man in dem Beispiel nur ungefähr jedes 5. Zeichen vergleichen muss. Statt 80 Millionen Vergleichen hätte man dann nur 16 Millionen.

Das Prinzip nennt sich bei Boyer-Moore die "Bad-Character-Heuristik", siehe https://de.wikipedia.org/wiki/Boyer-Moore-Algorithmus.

Sinspin 20. Nov 2024 07:09

AW: Alternative zu PosEx
 
Zitat:

Zitat von Amateurprofi (Beitrag 1543276)
Nochmal zu "Wie sieht es denn mit einer 64Bit Variante aus?"
Warum fragst du dass wenn du sagst "Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre."

:gruebel: Warum soll ich für meinen üblichen Kleinkram das Alte weiter verwenden wenn ich was Neues haben könnte?
Dann muss der Kopp sich auch nicht merken das es da mal was schnelles gegeben hat was man verwenden könnte. Es geht einfach.

Aber ich brauche eben 64Bit, da alle meine Komponenten auf 64Bit umgestellt sind und ich nur noch 64Bit Programme schreibe.

Amateurprofi 21. Nov 2024 03:34

AW: Alternative zu PosEx
 
Zitat:

Zitat von Sinspin (Beitrag 1543285)
Zitat:

Zitat von Amateurprofi (Beitrag 1543276)
Nochmal zu "Wie sieht es denn mit einer 64Bit Variante aus?"
Warum fragst du dass wenn du sagst "Ich habe aber bisher noch keinen Anwendungsfall gehabt bei dem die Untersuchung eines Strings zeitkritisch gewesen wäre."

:gruebel: Warum soll ich für meinen üblichen Kleinkram das Alte weiter verwenden wenn ich was Neues haben könnte?
Dann muss der Kopp sich auch nicht merken das es da mal was schnelles gegeben hat was man verwenden könnte. Es geht einfach.

Aber ich brauche eben 64Bit, da alle meine Komponenten auf 64Bit umgestellt sind und ich nur noch 64Bit Programme schreibe.

Werde die Tage 64 Bit-Versionen schreiben.

Amateurprofi 23. Nov 2024 12:33

AW: Alternative zu PosEx
 
Hier sind auch die 64bit-Versionen von StrPos und StrPosEx.
Der Vollständigkeit halber und wegen marginaler Änderungen auch noch mal die 32bit-Versionen.

StrPos

Delphi-Quellcode:
{$IFDEF CPUX86}
{------------------------------------------------------------------------------}
{ StrPos 32bit                                                                }
{ Prüft, ob SearchFor in SearchIn innerhalb des Bereiches First..Last         }
{ vorkommt und gibt die gefundene Position. bzw 0 für 'nicht gefunden' zurück. }
{ First<1 heißt ab Anfang von SearchIn suchen.                                }
{ Last<1 oder Last>Length(SearchIn) heißt bis Ende von SearchIn suchen.       }
{------------------------------------------------------------------------------}
FUNCTION StrPos(const SearchFor,SearchIn:String; First,Last:NativeInt):Integer;
asm
               // EAX=@SearchFor, EDX=@SearchIn, ECX=First, Stack=Last
               test eax,eax
               jz   @Nil             // SearchFor leer
               test edx,edx
               jz   @Nil             // SearchIn leer
               dec  ecx              // First 0-Based
               jge  @FirstOk         // First>=1
               xor  ecx,ecx
@FirstOK:     push ebx
               push edi
               push esi
               mov  ebp,Last
               mov  ebx,[edx-4]      // Length(SearchIn)
               test ebp,ebp
               jle  @LastOK          // Last<=0 heißt bis Ende suchen
               cmp  ebp,ebx
               jge  @LastOK          // Last>Length(SearchIn), bis Ende suchen
               mov  ebx,ebp          // Nur bis Last suchen
@LastOK:      // EAX=@SearchFor, EDX=@SearchIn, ECX=First-1, EBX=Last
               mov  edi,[eax-4]      // Length(SearchFor)
               lea  esi,[ecx+edi]    // First+Length(SearchFor)-1
               cmp  esi,ebx
               jg   @Past            // First>Last oder First+Length(SearchFor)>Last
               lea  eax,[eax+edi*2-2] // @SearchFor[Length(SearchFor)]
               lea  ebp,[edx+ebx*2]  // @SearchIn[Last+1]
               lea  edx,[edx+esi*2-2] // @SearchIn[First+Length(SearchFor)-1]
               lea  edi,[edi*2-2]    // 2*(Length(SearchFor)-1)
               neg  edi              // -(2*(Length(SearchFor)-1))
               add  ecx,ecx          // 2*(First-1)
               sub  ecx,edx          // 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               push ecx              // Für spätere Positionsberechnung
               movzx ecx,word[eax]    // letztes Zeichen von SearchFor
               // --------------------------------------------------------------
               // CX = Letztes Zeichen von SearchFor
               // EDX = SearchIn[First+Length(SearchFor)
               // EBP = @SearchIn[Last+1 Zeichen]
               // EDI = -(2*(Length(SearchFor)-1))
               // ESI = First+Length(SearchFor)-1
@Loop:        cmp  cx,[edx]         // Letzes Zeichen von SearchFor an EDX?
               jz   @Test0            // Ja, SearchIn auf voriges Zeichen
@AfterTest0:  cmp  cx,[edx+2]       // Letzes Zeichen von SearchFor an EDX+1 Zeichen
               jz   @TestT           // J,
@AfterTestT:  add  edx,8             // SearchIn+4 Zeichen
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  edx,-4            // SearchIn-2 Zeichen
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @False           // Nicht gefunden
@Continue:    cmp  cx,[edx-4]       // Letzes Zeichen von SearchFor an EDX-2 Zeichen?
               jz   @Test2            // Ja, SearchIn -3 Zeichen
               cmp  cx,[edx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  edx,2             // SearchIn + 1 Zeichen, durch folgende Adds - 2 Zeichen
@Test2:       add  edx,-4            // SearchIn - 2 Zeichen, durch folgendes Add - 3 Zeichen
@Test0:       add  edx,-2            // SearchIn - 1 Zeichen
@TestT:       mov  esi,edi
               test esi,esi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
@String:      mov  ebx,[eax+esi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               cmp  esi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[eax+esi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               add  esi,8             // Zeichenzahl + 4 Zeichen
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
@Found:       lea  eax,[edx+4]      // Fundstelle
               cmp  eax,ebp          // Im zu durchsuchenden Bereich?
               ja   @False           // Nein, nicht gefunden.
               add  eax,[esp]        // Endgültige Position in Bytes
               shr  eax,1             // Endgültige Position in Zeichen
               jmp  @End             // Stack bereinigen, Register wieder herstellen
               //---------------------------------------------------------------
@False:       xor  eax,eax          // Nicht gefunden
               jmp  @End             // Stack bereinigen, Register wieder herstellen
               //---------------------------------------------------------------
@Nil:         xor  eax,eax          // Nicht gefunden
               jmp  @Ret             // Return
               //---------------------------------------------------------------
@Past:        xor  eax,eax          // Nicht gefunden
               jmp  @Pop             // Register wieder herstellen
               //---------------------------------------------------------------
@End:         add  esp,4             // Stack bereinigen
@Pop:         pop  esi              // ESI wieder herstellen
               pop  edi              // EDI wieder herstellen
               pop  ebx              // EBX wieder herstellen
@Ret:
end;
{$ENDIF}
Delphi-Quellcode:
{$IFDEF CPUX64}
{------------------------------------------------------------------------------}
{ StrPos 64Bit                                                                }
{ Prüft, ob SearchFor in SearchIn innerhalb des Bereiches First..Last         }
{ vorkommt und gibt die gefundene Position. bzw 0 für 'nicht gefunden' zurück. }
{ First<1 heißt ab Anfang von SearchIn suchen.                                }
{ Last<1 oder Last>Length(SearchIn) heißt bis Ende von SearchIn suchen.       }
{------------------------------------------------------------------------------}
FUNCTION StrPos(const SearchFor,SearchIn:String; First,Last:NativeInt):Integer;
asm
               // RCX=@SearchFor, RDX=@SearchIn, R8=First, R9=Last
               test rcx,rcx
               jz   @Nil             // SearchFor leer
               test edx,edx
               jz   @Nil             // SearchIn leer
               dec  r8                // First 0-Based
               jge  @FirstOk         // First>=1
               xor  r8,r8
@FirstOK:     movsx r10,dword[rdx-4] // Length(SearchIn)
               test r9,r9
               jle  @LastOK          // Last<=0 heißt bis Ende suchen
               cmp  r9,r10
               jge  @LastOK          // Last>Length(SearchIn), bis Ende suchen
               mov  r10,r9            // Nur bis Last suchen
@LastOK:      movsx r9,dword[rcx-4]  // Length(SearchFor)
               add  r8,r9             // First+Length(SearchFor)-1
               cmp  r8,r10
               jg   @Nil             // First>Last oder First+Length(SearchFor)>Last
               push rbx              // RBX retten
               lea  rax,[rcx+r9*2-2] // @SearchFor[Length(SearchFor)]
               lea  r11,[rdx+r10*2]  // @SearchIn[Last+1]
               lea  rdx,[rdx+r8*2-2] // @SearchIn[First+Length(SearchFor)-1]
               lea  r9,[r9*2-2]      // 2*(Length(SearchFor)-1)
               neg  r9                // -(2*(Length(SearchFor)-1))
               add  r8,r8             // 2*(First-1)
               sub  r8,rdx           // 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               mov  cx,[rax]         // Letztes Zeichen von SearchFor
               // --------------------------------------------------------------
               // RAX @SearchFor[Length(SearchFor)]
               // RDX = Aktuelle Fundstelle für letztes Zeichen von SearchFor
               // CX = Letztes Zeichen von SearchFor
               // R8  = 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               // R9  = -(2*(Length(SearchFor)-1))
               // R11 = Terminator
@Loop:        cmp  cx,[rdx]         // Letzes Zeichen von SearchFor an EDX?
               jz   @Test0            // Ja, SearchIn auf voriges Zeichen
@AfterTest0:  cmp  cx,[rdx+2]       // Letzes Zeichen von SearchFor an EDX+1 Zeichen
               jz   @TestT           // J,
@AfterTestT:  add  rdx,8             // SearchIn+4 Zeichen
               cmp  rdx,r11           // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  rdx,-4            // SearchIn-2 Zeichen
               cmp  rdx,r11           // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NotFound        // Nicht gefunden
@Continue:    cmp  cx,[rdx-4]       // Letzes Zeichen von SearchFor an EDX-2 Zeichen?
               jz   @Test2            // Ja, SearchIn -3 Zeichen
               cmp  cx,[rdx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  rdx,2             // SearchIn + 1 Zeichen, durch folgende Adds - 2 Zeichen
@Test2:       add  rdx,-4            // SearchIn - 2 Zeichen, durch folgendes Add - 3 Zeichen
@Test0:       add  rdx,-2            // SearchIn - 1 Zeichen
@TestT:       test r9,r9             // Hat SearchFor nur 1 Zeichen?
               jz   @Found           // Ja, gefunden
               mov  r10,r9
@String:      mov  ebx,[rax+r10]    // 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+r10+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               cmp  r10,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[rax+r10+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+r10+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               add  r10,8             // Zeichenzahl + 4 Zeichen
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
@Found:       lea  rax,[rdx+4]      // Fundstelle
               cmp  rax,r11           // Im zu durchsuchenden Bereich?
               ja   @NotFound        // Nein, nicht gefunden.
               add  rax,r8            // Endgültige Position in Bytes
               shr  rax,1             // Endgültige Position in Zeichen
               pop  rbx              // RBX wieder herstellen
               ret
               //---------------------------------------------------------------
@Nil:         xor  eax,eax
               ret
@NotFound:    //---------------------------------------------------------------
               xor  eax,eax          // Nicht gefunden
               pop  rbx              // RBX wieder herstellen
end;
{$ENDIF}
StrPosEx

Delphi-Quellcode:
{$IFDEF CPUX86}
{------------------------------------------------------------------------------}
{ StrPosEx 32bit                                                              }
{ Sucht alle Vorkommen von SearchFor in SearchIn und stellt die Positionen    }
{ der Fundstellen in Positions.                                               }
{ Es ist Sache der aufrufenden Stelle, sicherzustellen, daas Positions lang   }
{ genug ist, alle Fundstellen zu speichern.                                   }
{ Parameter                                                                   }
{    SearchFor : String, nach dem gesucht wird.                              }
{    SearchIn  : String, in dem gesucht wird.                                }
{    Positions : Array zur Speicherung der Fundstellen.                      }
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                 0 = Keine, -1 = Alle.                                       }
{                    Bit 0 = 1 : Wenn SearchFor leer ist.                     }
{                    Bit 1 = 1 : Wenn SearchIn leer ist.                      }
{                    Bit 2 = 1 : Wenn SearchFor länger ist, als SearchIn.     }
{                    Bit 3 = 1 : Wenn Positions nicht assigned ist.           }
{                    Bit 4 = 1 : Wenn Positions zu kurz ist.                  }
{                 Wenn ein Fehler auftritt, und das korrespondierende Bit in  }
{                 Exceptions nicht gesetzt ist, werden folgende Fehlercodes   }
{                 zurückgegeben:                                              }
{                    -1 SearchFor leer.                                       }
{                    -2 SearchIn leer.                                        }
{                    -3 SearchFor länger als SearchIn.                        }
{                    -4 Positions nicht assigned.                             }
{                    -5 Positions zu kurz.                                    }
{ Wenn kein Fehler auftritt, wird die Anzahl der Fundstellen zurückgegeben.   }
{------------------------------------------------------------------------------}
FUNCTION StrPosEx(const SearchFor,SearchIn:String;
   Positions:TIntegerDynArray; Exceptions:NativeInt=0):Integer;
const
   sSearchForEmpty:String='StrPosEx:'#13'SearchFor ist leer';
   sSearchInEmpty:String='StrPosEx:'#13'SearchIn ist leer';
   sSearchForTooLong:String='StrPosEx:'#13'Searchfo ist länger als SearchIn';
   sPositionsEmpty:String='StrPosEx:'#13'Positions ist nicht assigned';
   sPositionsLength:String='StrPosEx:'#13'Das Array "Positions" ist zu kurz '+
                           'um alle Fundstellen zu speichern';
   pExClass:ExceptClass=(Exception);
asm
               // EAX=@SearchFor, EDX=@SearchIn, ECX=Positions
               // Register retten und Platz für lokale Variablen reservieren
               push ebp
               push ebx
               push edi
               push esi
               sub  esp,12            // Platz für 3 Integers
               // Prüfen, ob SearchFor und SearchIn nicht leer sind
               test eax,eax          // SearchFor leer?
               jz   @SFNil           // Ja, Fehlermedung
               test edx,edx          // SearchIn leer?
               jz   @SINil           // Ja, Fehlermeldung
               test ecx,ecx          // Positions leer?
               jz   @PosNil          // Ja, Fehlermeldung
               // Längen von SearchFor und SearchIn laden und prüfen ob
               // SearchFor nicht länger ist, als SearchIn
               mov  edi,[eax-4]      // Length(SearchFor)
               mov  ebx,[edx-4]      // Length(SearchIn)
               cmp  edi,ebx          // SearchFor länger als SearchIn?
               ja   @SFTooLong       // Ja, Fehlermeldung
               cmp  edi,1             // Hat SearchFor nur 1 Zeichen
               je   @Char            // Ja
               // Positions retten, Anzahl Fundstellen auf 0
               mov  [esp+8],ecx      // Positions
               mov  [esp+4],0         // Anzahl Fundstellen
               // Zeiger und Länge von SearchIn initialsieren
               lea  eax,[eax+edi*2-2] // EAX auf letztes Zeichen in SearchFor
               lea  ebp,[edx+ebx*2]  // EBP hinter letztes Zeichen von SearchIn
               lea  edx,[edx+edi*2-2] // EDX auf Ende der ersten potentiellen Fundstelle
               lea  edi,[edi*2-2]    // EDI = 2*(Length(SearchFor)-1)
               neg  edi              // EDI = -(2*(Length(SearchFor)-1))
               xor  ecx,ecx
               sub  ecx,edx          // ECX = -SearchIn[Length(SearchFor)-1]
               mov  [esp],ecx        // Für spätere Positionsberechnung
               // --------------------------------------------------------------
               // EAX    = Zeigt auf letztes Zeichen in SearchFor
               // EDX    = Zeigt auf Ende der nächsten potentiellen Fundstelle
               // EBP    = Zeigt hinter letztes Zeichen von SearchIn
               // EDI    = -(2*(Length(SearchFor)-1))
               // [ESP-8] = Postions
               // [ESP-4] = Anzahl Fundstellen
               // [ESP]  = -SearchIn[Length(SearchFor)-1]
@Start:       mov  cx,[eax]         // letztes Zeichen von SearchFor
@Loop:        cmp  cx,[edx]         // CX an [EDX]?
               jz   @Test0            // Ja, weitere Zeichen ab [EDX-1 Char] prüfen
@AfterTest0:  cmp  cx,[edx+2]       // CX an [EDX + 1 Char]?
               jz   @TestT           // Ja, weitere Zeichen ab [EDX] prüfen
@AfterTestT:  add  edx,8             // SearchIn + 4 Chars
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  edx,-4            // SearchIn - 2 Chars
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NoFurther       // Keine weiteren Fundstellen
@Continue:    cmp  cx,[edx-4]       // CX an [EDX - 2 Chars]?
               jz   @Test2            // Ja, SearchIn - 3 Chars
               cmp  cx,[edx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  edx,2             // SearchIn + 1 Char, durch folgende Adds - 2 Chars
@Test2:       add  edx,-4            // SearchIn - 2 Chars, durch folgendes Add - 3 Chars
@Test0:       add  edx,-2            // SearchIn - 1 Char
@TestT:       test edi,edi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
               mov  esi,edi          // -(2*(Length(SearchFor)-1))
@String:      mov  ebx,[eax+esi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               cmp  esi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[eax+esi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               add  esi,8             // Zeichenzahl + 4 Chars
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
               // Gefunden. EDX zeigt auf Fundstelle - 1 Zeichen
@Found:       lea  ecx,[edx+4]      // Fundstelle + 1 Zeichen
               cmp  ecx,ebp          // Im zu durchsuchenden Bereich?
               ja   @NoFurther       // Nein, keine weiteren Fundstellen
               add  ecx,[esp]        // Position in Bytes
               shr  ecx,1             // Position in Zeichen
               mov  esi,[esp+8]      // Positions
               mov  ebx,[esp+4]      // Bisherige Anzahl Fundstellen
               cmp  ebx,[esi-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               mov  [esi+ebx*4],ecx  // Fundstelle speichern
               inc  ebx              // Anzahl Fundstellen + 1
               mov  [esp+4],ebx      // Anzahl Fundstellen speichern
               // EDX auf nächste potentielle Fundstelle
               mov  esi,edi          // -(2*(Length(SearchFor)-1))
               neg  esi              //   2*(Length(SearchFor)-1)
               lea  edx,[edx+esi+4]  // EDX=nächste potentielle Fundstelle
               cmp  edx,ebp          // Noch im gültigen Bereich?
               jb   @Start           // Ja, weiter suchen
               jmp  @NoFurther       // Nein, keine weiteren Fundstellen
               // --------------------------------------------------------------
               // Suche nach nur einem Zeichen
               // EAX = SearchFor
               // EDX = SearchIn
               // ECX = Positions
               // EBX = Lenght(SearchIn)
@Char:        mov  ebp,ecx          // Positions
               xor  ecx,ecx          // Anzahl Fundstellen
               lea  edx,[edx+ebx*2]  // Hinter letztes Zeichen von SearchIn
               lea  edi,[ebx+1]      // für spötere Positionsermittlung
               neg  ebx
               mov  ax,[eax]         // gesuchtes Zeichen
@CharLoop:    cmp  ax,[edx+ebx*2]   // AX an aktueller Position?
               jz   @CharFound1       // Ja
               cmp  ax,[edx+ebx*2+2] // AX an nächster Position?
               jz   @CharFound2       // Ja
               add  ebx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               // An [EDX+EBX*2] gefunden
@CharFound1:  cmp  ecx,[ebp-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  esi,[edi+ebx]    // Fundstelle
               mov  [ebp+ecx*4],esi  // In Positions speichern
               inc  ecx              // Anzahl Fundstellen
               inc  ebx              // 1 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               // An [EDX+EBX*2+2] gefunden
@CharFound2:  cmp  ecx,[ebp-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  esi,[edi+ebx+1]  // Fundstelle * 2
               mov  [ebp+ecx*4],esi  // In Positions speichern
               inc  ecx              // Anzahl Fundstellen
               add  ebx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               //---------------------------------------------------------------
               // SearchFor ist leer
@SFNil:       mov  eax,sSearchForEmpty
               mov  ecx,1             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchIn ist leer
@SINil:       mov  eax,sSearchInEmpty
               mov  ecx,2             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchFor ist länger als SearchIn
@SFTooLong:   mov  eax,sSearchForTooLong
               mov  ecx,3             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions ist nicht assigned
@PosNil:      mov  eax,sPositionsEmpty
               mov  ecx,4             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions nicht lang genug um neue Fundstelle zu speichern
@OutOfMem:    mov  eax,sPositionsLength
               mov  ecx,5             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Keine weiteren Fundstellen
@NoFurther:   mov  ecx,[esp+4]      // Anzahl Fundstellen
@NoFurtherC:  xor  eax,eax          // ´Kein Fehler
               //---------------------------------------------------------------
               // Stack bereinigen und Register restaurieren
@End:         add  esp,12            // Stack bereinigen
               pop  esi              // ESI wieder herstellen
               pop  edi              // EDI wieder herstellen
               pop  ebx              // EBX wieder herstellen
               pop  ebp              // EBP wieder herstellen
               //---------------------------------------------------------------
               // Prüfen ob Fehler vorliegt und ggfs. Exception auslösen
               test eax,eax          // Fehler ?
               jz   @NoError         // Nein, Anzahl Fundstellen zurückgeben
               mov  edx,1
               shl  edx,cl
               neg  ecx              // Fehlercode
               shr  edx,1
               test [ebp+8],edx      // Exception werfen?
               jz   @NoError         // Nein, nur Fehlercode zurückgeben
               mov  ecx,eax          // Fehlertext
               mov  eax,pExClass     // InstanceOrVMT
               mov  edx,1             // Alloc
               call Exception.Create
               call System.@RaiseExcept
               //---------------------------------------------------------------
               // Anzahl Fundstellen bzw. Fehlercode in Result
@NoError:     mov  eax,ecx          // Anzahl Fundstellen bzw. FehlerCode
end;
{$ENDIF}
Delphi-Quellcode:
{$IFDEF CPUX64}
{------------------------------------------------------------------------------}
{ StrPosEx 64bit                                                              }
{ Sucht alle Vorkommen von SearchFor in SearchIn und stellt die Positionen    }
{ der Fundstellen in Positions.                                               }
{ Es ist Sache der aufrufenden Stelle, sicherzustellen, daas Positions lang   }
{ genug ist, alle Fundstellen zu speichern.                                   }
{ Parameter                                                                   }
{    SearchFor : String, nach dem gesucht wird.                              }
{    SearchIn  : String, inem gesucht wird.                                  }
{    Positions : Array zur Speicherung der Fundstellen.                      }
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                 0 = Keine, -1 = Alle                                        }
{                 In der aktuellen Version ist das Werfen von Exceptions      }
{                 deaktiviert, weil der Code hier nicht funktioniert.         }
{                 In anderen Anwendungen funktioniert er.                     }
{                    Bit 0 = 1 : Wenn SearchFor leer ist.                     }
{                    Bit 1 = 1 : Wenn SearchIn leer ist.                      }
{                    Bit 2 = 1 : Wenn SearchFor länger ist, als SearchIn.     }
{                    Bit 3 = 1 : Wenn Positions nicht assigned ist.           }
{                    Bit 4 = 1 : Wenn Positions zu kurz ist.                  }
{                 Wenn ein Fehler auftritt, und das korrespondierende Bit in  }
{                 Exceptions nicht gesetzt ist, werden folgende Fehlercodes   }
{                 zurückgegeben:                                              }
{                    -1 SearchFor leer.                                       }
{                    -2 SearchIn leer.                                        }
{                    -3 SearchFor länger als SearchIn.                        }
{                    -4 Positions nicht assigned.                             }
{                    -5 Positions zu kurz.                                    }
{ Wenn kein Fehler auftritt, wird die Anzahl der Fundstellen zurückgegeben.   }
{------------------------------------------------------------------------------}
FUNCTION StrPosEx(const SearchFor,SearchIn:String;
   Positions:TIntegerDynArray; Exceptions:NativeInt=0):Integer;
const
   sSearchForEmpty:String='StrPosEx:'#13'SearchFor ist leer';
   sSearchInEmpty:String='StrPosEx:'#13'SearchIn ist leer';
   sSearchForTooLong:String='StrPosEx:'#13'Searchfo ist länger als SearchIn';
   sPositionsEmpty:String='StrPosEx:'#13'Positions ist nicht assigned';
   sPositionsLength:String='StrPosEx:'#13'Das Array "Positions" ist zu kurz '+
                           'um alle Fundstellen zu speichern';
   pExClass:ExceptClass=(Exception);
asm
               // RCX=@SearchFor, RDX=@SearchIn, R8=Positions, R9=Exceptions
               // Exceptions = 0 (keine Exceptions) setzen, weil das hier
               // nicht funktioniert (In anderen Projektion funktionierts).
               xor  r9,r9             // Keine Exceptions
               // Register retten
               push rbp
               push rbx
               push rdi
               push rsi
               push r12
               // Prüfen, ob SearchFor und SearchIn nicht leer sind
               test rcx,rcx          // SearchFor leer?
               jz   @SFNil           // Ja, Fehlermedung
               test rdx,rdx          // SearchIn leer?
               jz   @SINil           // Ja, Fehlermeldung
               test r8,r8             // Positions assigned?
               jz   @PosEmpty        // Nein, Fehlermeldung
               // Längen von Positons, SearchFor und SearchIn laden und
               // prüfen ob SearchFor nicht länger ist, als SearchIn
               movsx rdi,dword[rcx-4] // Length(SearchFor)
               movsx rbx,dword[rdx-4] // Length(SearchIn)
               cmp  edi,ebx          // SearchFor länger als SearchIn?
               ja   @SFTooLong       // Ja, Fehlermeldung
               mov  r11,[r8-8]       // Length Positions
               xor  r10,r10           // Anzahl Fundstellen auf 0
               cmp  edi,1             // Hat SearchFor nur 1 Zeichen
               je   @Char            // Ja
               // Zeiger und Länge von SearchIn initialsieren
               lea  rax,[rcx+rdi*2-2] // RAX auf letztes Zeichen in SearchFor
               lea  rbp,[rdx+rbx*2]  // RBP hinter letztes Zeichen von SearchIn
               lea  rdx,[rdx+rdi*2-2] // RDX auf Ende der ersten potentiellen Fundstelle
               lea  rdi,[rdi*2-2]    // RDI = 2*(Length(SearchFor)-1)
               neg  rdi              // RDI = -(2*(Length(SearchFor)-1))
               xor  r12,r12
               sub  r12,rdx          // R12 = -SearchIn[Length(SearchFor)-1]
               // --------------------------------------------------------------
               // RAX    = Zeigt auf letztes Zeichen in SearchFor
               // RDX    = Zeigt auf Ende der nächsten potentiellen Fundstelle
               // RBP    = Zeigt hinter letztes Zeichen von SearchIn
               // RDI    = -(2*(Length(SearchFor)-1))
               // R8      = Postions
               // R9      = Exceptions
               // R10     = Anzahl Fundstellen
               // R11     = Length(Positions)
               // R12     = -SearchIn[Length(SearchFor)-1]
               //           Für Ermittlung der Positionen von Fundstellen
@Start:       mov  cx,[rax]         // letztes Zeichen von SearchFor
@Loop:        cmp  cx,[rdx]         // CX an [EDX]?
               jz   @Test0            // Ja, weitere Zeichen ab [EDX-1 Char] prüfen
@AfterTest0:  cmp  cx,[rdx+2]       // CX an [EDX + 1 Char]?
               jz   @TestT           // Ja, weitere Zeichen ab [EDX] prüfen
@AfterTestT:  add  rdx,8             // SearchIn + 4 Chars
               cmp  rdx,rbp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  rdx,-4            // SearchIn - 2 Chars
               cmp  rdx,rbp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NoFurther       // Keine weiteren Fundstellen
@Continue:    cmp  cx,[rdx-4]       // CX an [EDX - 2 Chars]?
               jz   @Test2            // Ja, SearchIn - 3 Chars
               cmp  cx,[rdx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  rdx,2             // SearchIn + 1 Char, durch folgende Adds - 2 Chars
@Test2:       add  rdx,-4            // SearchIn - 2 Chars, durch folgendes Add - 3 Chars
@Test0:       add  rdx,-2            // SearchIn - 1 Char
@TestT:       test rdi,rdi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
               mov  rsi,rdi          // -(2*(Length(SearchFor)-1))
@String:      mov  ebx,[rax+rsi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+rsi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               cmp  rsi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[rax+rsi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+rsi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               add  rsi,8             // Zeichenzahl + 4 Chars
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
               // Gefunden. RDX zeigt auf Fundstelle - 1 Zeichen
@Found:       lea  rsi,[rdx+4]      // Fundstelle + 1 Zeichen
               cmp  rsi,rbp          // Im zu durchsuchenden Bereich?
               ja   @NoFurther       // Nein, keine weiteren Fundstellen
               add  rsi,r12           // Position in Bytes
               shr  rsi,1             // Position in Zeichen
               cmp  r10,r11           // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               mov  [r8+r10*4],esi   // Fundstelle speichern
               inc  r10               // Anzahl Fundstellen + 1
               // RDX auf nächste potentielle Fundstelle
               mov  rsi,rdi          // -(2*(Length(SearchFor)-1))
               neg  rsi              //   2*(Length(SearchFor)-1)
               lea  rdx,[rdx+rsi+4]  // EDX=nächste potentielle Fundstelle
               cmp  rdx,rbp          // Noch im gültigen Bereich?
               jb   @Loop            // Ja, weiter suchen
               jmp  @NoFurther       // Nein, keine weiteren Fundstellen
               // --------------------------------------------------------------
               // Suche nach nur einem Zeichen
               // RCX = SearchFor
               // RDX = SearchIn
               // RBX = Lenght(SearchIn)
               // R8  = Positions
               // R9  = Exceptions
               // R10 = Anzahl Fundstellen
               // R11 = Length(Positions)
@Char:        lea  rdx,[rdx+rbx*2]  // Hinter letztes Zeichen von SearchIn
               lea  rdi,[rbx+1]      // für spätere Positionsermittlung
               neg  rbx
               mov  ax,[rcx]         // gesuchtes Zeichen
@CharLoop:    cmp  ax,[rdx+rbx*2]   // AX an aktueller Position?
               jz   @CharFound1       // Ja
               cmp  ax,[rdx+rbx*2+2] // AX an nächster Position?
               jz   @CharFound2       // Ja
               add  rbx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurther       // Keine weiteren Fundstellen
               // An [EDX+EBX*2] gefunden
@CharFound1:  cmp  r10,r11           // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  rsi,[rdi+rbx]    // Fundstelle
               mov  [r8+r10*4],esi   // In Positions speichern
               inc  r10               // Anzahl Fundstellen
               inc  rbx              // 1 Zeichen weiter
               jl   @CharLoop        // Weiter, solange negativ
               jmp  @NoFurther       // Keine weiteren Fundstellen
               // An [EDX+EBX*2+2] gefunden
@CharFound2:  cmp  r10,r11           // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  rsi,[rdi+rbx+1]  // Fundstelle
               mov  [r8+r10*4],esi   // In Positions speichern
               inc  r10               // Anzahl Fundstellen
               add  rbx,2             // 2 Zeichen weiter
               jl   @CharLoop        // Weiter, solange negativ
               jmp  @NoFurther       // Keine weiteren Fundstellen
               //---------------------------------------------------------------
               // SearchFor ist leer
@SFNil:       mov  r8,sSearchForEmpty
               mov  ecx,1             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchIn ist leer
@SINil:       mov  r8,sSearchInEmpty
               mov  ecx,2             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchFor ist länger als SearchIn
@SFTooLong:   mov  r8,sSearchForTooLong
               mov  ecx,3             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions ist nicht assigned
@PosEmpty:    mov  r8,sPositionsEmpty
               mov  ecx,4             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Positions nicht lang genug um neue Fundstelle zu speichern
@OutOfMem:    mov  r8,sPositionsLength
               mov  ecx,5             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Keine weiteren Fundstellen
@NoFurther:   xor  r8,r8             // ´Kein Fehler
               mov  rcx,r10           // Anzahl Fundstellen
               //---------------------------------------------------------------
               // Register restaurieren
@End:         pop  r12               // R12 wieder herstellen
               pop  rsi              // RSI wieder herstellen
               pop  rdi              // RDI wieder herstellen
               pop  rbx              // RBX wieder herstellen
               pop  rbp              // RBP wieder herstellen
               //---------------------------------------------------------------
               // Prüfen ob Fehler vorliegt und ggfs. Exception auslösen
               test r8,r8             // Fehler ?
               jz   @NoError         // Nein, Anzahl Fundstellen zurückgeben
               mov  edx,1
               shl  edx,cl
               neg  ecx              // Fehlercode
               shr  edx,1
               test r9d,edx          // Exception werfen?
               jz   @NoError         // Nein, nur Fehlercode zurückgeben
               // Exception auslösen
               push rcx              // Fehlercode retten
               push rbp
               sub  rsp,$20
               mov  rbp,rsp
               mov  rcx,pExClass     // InstanceOrVMT
               mov  dl,$01            // Alloc
               call Exception.Create
               mov  rcx,rax
               call System.@RaiseExcept
               add  rsp,$20
               pop  rbp
               pop  rcx              // Fehlercode
               //---------------------------------------------------------------
               // Anzahl Fundstellen bzw. Fehlercode in Result
@NoError:     mov  eax,ecx          // Anzahl Fundstellen bzw. FehlerCode
end;
{$ENDIF}

Sinspin 25. Nov 2024 09:20

AW: Alternative zu PosEx
 
Krass. Danke.

Sieht so aus als wenn ich malwider ein Buch in die Hand nehmen sollte und ein bisschen was zum Thema Assembler lesen. Ich fühle mich gerade irgendwie outdated.
Delphi-Quellcode:
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                 0 = Keine, -1 = Alle                                        }
{                 In der aktuellen Version ist das Werfen von Exceptions      }
{                 deaktiviert, weil der Code hier nicht funktioniert.         }
{                 In anderen Anwendungen funktioniert er.                     }
Was meinst du damit? Fehlererkennung geht nicht oder Exception auswerfen geht nicht in 64Bit?

Amateurprofi 25. Nov 2024 10:35

AW: Alternative zu PosEx
 
Zitat:

Zitat von Sinspin (Beitrag 1543479)
Krass. Danke.

Sieht so aus als wenn ich malwider ein Buch in die Hand nehmen sollte und ein bisschen was zum Thema Assembler lesen. Ich fühle mich gerade irgendwie outdated.
Delphi-Quellcode:
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                 0 = Keine, -1 = Alle                                        }
{                 In der aktuellen Version ist das Werfen von Exceptions      }
{                 deaktiviert, weil der Code hier nicht funktioniert.         }
{                 In anderen Anwendungen funktioniert er.                     }
Was meinst du damit? Fehlererkennung geht nicht oder Exception auswerfen geht nicht in 64Bit?

Ich meinte damit genau das was ich schrieb:
"Das Werfen von Exceptions ist deaktiviert."
Selbstverständlich werden Fehler erkannt, aber es wird auch dann keine Exception ausgelöst, wenn der Parameter "Exception" vorgibt, dass eine Exception ausgelöst werden soll, sondern ein entsprechender Fehlercode zurückgegeben.
Der Funktion gibt ja die Anzahl der Fundstellen zurück, die >= 0 ist.
Bei Fehlern ist das Ergebnis der Funktion negativ und kann sein:
-1 SearchFor (der zu suchende Text) ist leer.
-2 SearchIn (der zu durchsuchende Text) ist leer.
-3 SearchFor ist länger als SearchIn.
-4 Positions ist Nil (nicht assigned)
-5 Positions ist zu kurz, um alle Fundstellen zu speichern.
Zu -4 und -5
Es wäre natürlich eleganter, wenn die Länge von Positions innerhalb der Funktion gesetzt würde, aber das, was in Delphi nur ein simples "SetLength(Positions,M)" ist, scheint in Assembler nicht so trivial zu sein.
Na klar könnte ich das mit einer ausgelagerten Delphi-Prozedur lösen.
Auch das Werfen von Exceptions wäre mit einer ausgelagerten Delphi-Prozedur leicht zu machen.
Aber meine Philosophie ist halt, dass eine Assembler-Prozedur keine externen Subroutinen aufruft.

Sinspin 25. Nov 2024 11:52

AW: Alternative zu PosEx
 
Nene :
Delphi-Quellcode:

Exceptions ... deaktiviert, weil der Code hier nicht funktioniert.
}
Ist aber eigentlich wurscht. Die meißten Exceptions enstehen eh durch unsaubere Programmierung. Sind also vermeidbar.
Ich arbeite aber eh lieber mit detaillierten Fehlercodes anstatt von Exceptions.

Setlength ist nicht trivial und sollte generell so selten wie möglich für das gleiche Array aufgerufen werden.
Verkettete Listen sind da deutlich praktischer. Kommen aber mit zusätzlichem Speicherverbrauch einher und sind beim kopieren hässlich.
Ich kann schon verstehen warum die Parameter sind wie sie sind.

Stevie 26. Nov 2024 12:25

AW: Alternative zu PosEx
 
64bit StrPos ist defekt.

Teste selbst:
Delphi-Quellcode:
StrPos('world', 'hello world', 0, 11)
liefert 12 und nicht 7

Blup 26. Nov 2024 15:53

AW: Alternative zu PosEx
 
Ich habe StrPosEx (32Bit) getestet und einen Fehler gefunden:
Code:
SetLength(Positions, 3)
StrPosEx('PaPa', 'PaPaPa', Positions)
Result = 1
Positions = [1,0,0]
Man muss das Array vor dem Aufruf auf die Anzahl der Ergebnisse anpassen.
Wenn der Platz nicht reicht, liefert die Funktion aber nicht die tatsächliche Anzahl zurück.
Die Fehlercodes sind für eine so simple Funktion eigentlich überflüssig, entweder es wird gefunden oder nicht.
Simple Überprüfungen kann man davor selbst vornehmen.

Meiner Meinung nach sollte die Funtktion den benötigten Platz selbst reservieren, z.B.:
Delphi-Quellcode:
function StrPosEx(const SearchFor, SearchIn: string; out Positions: TIntegerDynArray): Integer;
function StrPosEx(const SearchFor, SearchIn: string): TIntegerDynArray;

Amateurprofi 26. Nov 2024 16:45

AW: Alternative zu PosEx
 
Zitat:

Zitat von Blup (Beitrag 1543546)
Ich habe StrPosEx (32Bit) getestet und einen Fehler gefunden:
Code:
SetLength(Positions, 3)
StrPosEx('PaPa', 'PaPaPa', Positions)
Result = 1
Positions = [1,0,0]
Man muss das Array vor dem Aufruf auf die Anzahl der Ergebnisse anpassen.
Wenn der Platz nicht reicht, liefert die Funktion aber nicht die tatsächliche Anzahl zurück.
Die Fehlercodes sind für eine so simple Funktion eigentlich überflüssig, entweder es wird gefunden oder nicht.
Simple Überprüfungen kann man davor selbst vornehmen.

Meiner Meinung nach sollte die Funtktion den benötigten Platz selbst reservieren, z.B.:
Delphi-Quellcode:
function StrPosEx(const SearchFor, SearchIn: string; out Positions: TIntegerDynArray): Integer;
function StrPosEx(const SearchFor, SearchIn: string): TIntegerDynArray;

Und wo ist da der Fehler?
"PaPa" wird in "PaPaPa" an Position 1 gefunden.
Dann wird geprüft, ob ab hinter der Fundstelle, also ab Position 1+Length("PaPa") der Text "PaPa" noch einmal gefunden wird.
Das ist nicht der Fall, also wird korrekt 1 zurückgegeben.

Zu "Meiner Meinung nach sollte die Funtktion den benötigten Platz selbst reservieren".
Hast Du einen Vorschlag, wie das zu realisieren ist?

himitsu 26. Nov 2024 16:50

AW: Alternative zu PosEx
 
Zitat:

wie
Als VAR-Parameter und drinnen ein SetLength.


Per se ist OUT hier falsch, jedenfalls in Bezug auf Managed-Types, wie z.B. dynamische Arrays.
Zum Glück macht Delphi hier heimlich, und ohne was zu sagen, ein VAR daraus.

Bei OUT ist es möglich auch die Referenz zu ändern, was hier "eigentlich" zu einem Speicherleck führen würde, wenn vor dem Aufruf das Array einen Inhalt hätte.

Amateurprofi 26. Nov 2024 21:09

AW: Alternative zu PosEx
 
Zitat:

Zitat von Stevie (Beitrag 1543532)
64bit StrPos ist defekt.

Teste selbst:
Delphi-Quellcode:
StrPos('world', 'hello world', 0, 11)
liefert 12 und nicht 7

Bei mir nicht.

Delphi-Quellcode:
PROCEDURE TMain.Test;
var N:Integer;
begin
   //Teste selbst: StrPos('world', 'hello world', 0, 11) liefert 12 und nicht 7
   N:=StrPos('world', 'hello world', 0, 11);
   ShowMessage(IntToStr(N));
end;
Code:
---------------------------
Mersenne
---------------------------
7
---------------------------
OK
---------------------------

Amateurprofi 26. Nov 2024 22:01

AW: Alternative zu PosEx
 
Zitat:

Zitat von himitsu (Beitrag 1543558)
Zitat:

wie
Als VAR-Parameter und drinnen ein SetLength.


Per se ist OUT hier falsch, jedenfalls in Bezug auf Managed-Types, wie z.B. dynamische Arrays.
Zum Glück macht Delphi hier heimlich, und ohne was zu sagen, ein VAR daraus.

Bei OUT ist es möglich auch die Referenz zu ändern, was hier "eigentlich" zu einem Speicherleck führen würde, wenn vor dem Aufruf das Array einen Inhalt hätte.

Hast Du auch einen Vorschlag wie man das in einer Assembler-Routine realisieren kann (32BitAsm und 64BitAsm)?
Ich hatte das vor langer Zeit mal versucht.
Weiß nicht mehr woran es scheiterte, hab aber die unangenehme Erinnerung, dass ich es nicht hingekriegt habe, was aber nicht wirklich der Grund für meine Vorgehensweise war.
Ich hab die Funktion ja im Zusammenhang mit Mersenne-Primzahlen und den daraus abgeleiteten Perfekten Zahlen geschrieben.
Die zZ längste Zahl "(2^136279841-1) * 2^(136279841-1)" hat 82,048,640 Ziffern.
Rein theoretisch müsste ich, um auf der sicheren Seite zu liegen, das Array auf eine Länge von 82,048,640 setzen = 330MB.
Es ist aber so, dass mein Programm, dann wenn eine Zahl geladen wird, zählt, welche Ziffer wie oft vorkommt.
Am häufigsten, bei der og Zahl die 7 (8,202,366 Mal).
Also muss ich das Array "nur" auf eine Länge von 8,202,366 setzen = 33MB.
In einer allgemeingültigen Funktion ist eine Abschätzung, wie lang das Array sein muss eher nicht möglich.
Wenn ich wiederholte Längenänderungen vermeiden will müsste ich also für eine Suche nach einzelnen Zeichen die Länge auf die Länge des zu durchsuchenden Strings setzen, ZZ 330MB, bei den nächsten Mersenne-Primzahlen sicherlich > 1GB, und dann wird es problematisch.
Unabhängig davon wüsste ich aber gern, wie man ein SetLength in Assembler realisieren kann.

Michael II 26. Nov 2024 22:20

AW: Alternative zu PosEx
 
Zitat:

Zitat von Amateurprofi (Beitrag 1543563)
Zitat:

Zitat von Stevie (Beitrag 1543532)
64bit StrPos ist defekt.

Teste selbst:
Delphi-Quellcode:
StrPos('world', 'hello world', 0, 11)
liefert 12 und nicht 7

Bei mir nicht.

Delphi-Quellcode:
PROCEDURE TMain.Test;
var N:Integer;
begin
   //Teste selbst: StrPos('world', 'hello world', 0, 11) liefert 12 und nicht 7
   N:=StrPos('world', 'hello world', 0, 11);
   ShowMessage(IntToStr(N));
end;
Code:
---------------------------
Mersenne
---------------------------
7
---------------------------
OK
---------------------------

Bei mir mit 64 Bit Code aus #13 und deinem Test PROCEDURE TMain.Test; auch Output: 12.
Delphi 11U2, Win11pro, Intel i7-11800H .

himitsu 26. Nov 2024 23:21

AW: Alternative zu PosEx
 
Auch im Assembler kann man andere Funktionen aufrufen, wie z.B. das SetLength.

Nur gibt es nicht nur ein SetLength und meistens heißt es anders, siehe System.pas,
und im Assembler heißen die manchmal nochmal anders, also das was in Richtung Compilermagic geht, und wo man die System.pas eher als "Demo" ansehen sollte. (wenn ich mich recht erinnere, wurden dort einige Funktionen mit einem @ am Anfang des Namen aufgerufen)

Tipp: bastel dir eine Pascal-Funktion, für den Aufruf, und schau im Assembler, was der Compiler daraus gemacht hat.

TRomano 27. Nov 2024 07:02

AW: Alternative zu PosEx
 
call @DynArraySetLength ist der Aufruf von "SetLength". In Win32 musst Du wohl eax mit der Länge belegen und unter Win64 rcx.

Amateurprofi 27. Nov 2024 08:43

AW: Alternative zu PosEx
 
Zitat:

Zitat von Michael II (Beitrag 1543566)
Zitat:

Zitat von Amateurprofi (Beitrag 1543563)
Zitat:

Zitat von Stevie (Beitrag 1543532)
64bit StrPos ist defekt.

Teste selbst:
Delphi-Quellcode:
StrPos('world', 'hello world', 0, 11)
liefert 12 und nicht 7

Bei mir nicht.

Delphi-Quellcode:
PROCEDURE TMain.Test;
var N:Integer;
begin
   //Teste selbst: StrPos('world', 'hello world', 0, 11) liefert 12 und nicht 7
   N:=StrPos('world', 'hello world', 0, 11);
   ShowMessage(IntToStr(N));
end;
Code:
---------------------------
Mersenne
---------------------------
7
---------------------------
OK
---------------------------

Bei mir mit 64 Bit Code aus #13 und deinem Test PROCEDURE TMain.Test; auch Output: 12.
Delphi 11U2, Win11pro, Intel i7-11800H .

Oh mein Gott, bin ich blöd.
Stevie schieb 64bit und ich teste 32bit.
Bei 64bit auch bei mir 12 statt 7.

Werde den Fehler suchen und korrigieren.

Amateurprofi 27. Nov 2024 10:44

AW: Alternative zu PosEx
 
Hier noch einmal die Asm-Funktionen StrPos und StrPosEx.
Die "StrPos" 64Bit ist korrigiert.
Wer Fehler findet soll die bitte nicht behalten sondern hier melden.

StrPos

Delphi-Quellcode:
{$IFDEF CPUX86}
{------------------------------------------------------------------------------}
{ StrPos 32bit                                                                }
{ Prüft, ob SearchFor in SearchIn innerhalb des Bereiches First..Last         }
{ vorkommt und gibt die gefundene Position. bzw 0 für 'nicht gefunden' zurück. }
{ First<1 heißt ab Anfang von SearchIn suchen.                                }
{ Last<1 oder Last>Length(SearchIn) heißt bis Ende von SearchIn suchen.       }
{------------------------------------------------------------------------------}
FUNCTION StrPos(const SearchFor,SearchIn:String; First,Last:NativeInt):Integer;
asm
               // EAX=@SearchFor, EDX=@SearchIn, ECX=First, Stack=Last
               test eax,eax
               jz   @Nil             // SearchFor leer
               test edx,edx
               jz   @Nil             // SearchIn leer
               dec  ecx              // First 0-Based
               jge  @FirstOk         // First>=1
               xor  ecx,ecx
@FirstOK:     push ebx
               push edi
               push esi
               mov  ebp,Last
               mov  ebx,[edx-4]      // Length(SearchIn)
               test ebp,ebp
               jle  @LastOK          // Last<=0 heißt bis Ende suchen
               cmp  ebp,ebx
               jge  @LastOK          // Last>Length(SearchIn), bis Ende suchen
               mov  ebx,ebp          // Nur bis Last suchen
@LastOK:      // EAX=@SearchFor, EDX=@SearchIn, ECX=First-1, EBX=Last
               mov  edi,[eax-4]      // Length(SearchFor)
               lea  esi,[ecx+edi]    // First+Length(SearchFor)-1
               cmp  esi,ebx
               jg   @Past            // First>Last oder First+Length(SearchFor)>Last
               lea  eax,[eax+edi*2-2] // @SearchFor[Length(SearchFor)]
               lea  ebp,[edx+ebx*2]  // @SearchIn[Last+1]
               lea  edx,[edx+esi*2-2] // @SearchIn[First+Length(SearchFor)-1]
               lea  edi,[edi*2-2]    // 2*(Length(SearchFor)-1)
               neg  edi              // -(2*(Length(SearchFor)-1))
               add  ecx,ecx          // 2*(First-1)
               sub  ecx,edx          // 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               push ecx              // Für spätere Positionsberechnung
               movzx ecx,word[eax]    // letztes Zeichen von SearchFor
               // --------------------------------------------------------------
               // CX = Letztes Zeichen von SearchFor
               // EDX = SearchIn[First+Length(SearchFor)
               // EBP = @SearchIn[Last+1 Zeichen]
               // EDI = -(2*(Length(SearchFor)-1))
               // ESI = First+Length(SearchFor)-1
@Loop:        cmp  cx,[edx]         // Letzes Zeichen von SearchFor an EDX?
               jz   @Test0            // Ja, SearchIn auf voriges Zeichen
@AfterTest0:  cmp  cx,[edx+2]       // Letzes Zeichen von SearchFor an EDX+1 Zeichen
               jz   @TestT           // J,
@AfterTestT:  add  edx,8             // SearchIn+4 Zeichen
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  edx,-4            // SearchIn-2 Zeichen
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @False           // Nicht gefunden
@Continue:    cmp  cx,[edx-4]       // Letzes Zeichen von SearchFor an EDX-2 Zeichen?
               jz   @Test2            // Ja, SearchIn -3 Zeichen
               cmp  cx,[edx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  edx,2             // SearchIn + 1 Zeichen, durch folgende Adds - 2 Zeichen
@Test2:       add  edx,-4            // SearchIn - 2 Zeichen, durch folgendes Add - 3 Zeichen
@Test0:       add  edx,-2            // SearchIn - 1 Zeichen
@TestT:       mov  esi,edi
               test esi,esi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
@String:      mov  ebx,[eax+esi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               cmp  esi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[eax+esi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               add  esi,8             // Zeichenzahl + 4 Zeichen
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
@Found:       lea  eax,[edx+4]      // Fundstelle
               cmp  eax,ebp          // Im zu durchsuchenden Bereich?
               ja   @False           // Nein, nicht gefunden.
               add  eax,[esp]        // Endgültige Position in Bytes
               shr  eax,1             // Endgültige Position in Zeichen
               jmp  @End             // Stack bereinigen, Register wieder herstellen
               //---------------------------------------------------------------
@False:       xor  eax,eax          // Nicht gefunden
               jmp  @End             // Stack bereinigen, Register wieder herstellen
               //---------------------------------------------------------------
@Nil:         xor  eax,eax          // Nicht gefunden
               jmp  @Ret             // Return
               //---------------------------------------------------------------
@Past:        xor  eax,eax          // Nicht gefunden
               jmp  @Pop             // Register wieder herstellen
               //---------------------------------------------------------------
@End:         add  esp,4             // Stack bereinigen
@Pop:         pop  esi              // ESI wieder herstellen
               pop  edi              // EDI wieder herstellen
               pop  ebx              // EBX wieder herstellen
@Ret:
end;
{$ENDIF}
Delphi-Quellcode:
{$IFDEF CPUX64}
{------------------------------------------------------------------------------}
{ StrPos 64Bit                                                                }
{ Prüft, ob SearchFor in SearchIn innerhalb des Bereiches First..Last         }
{ vorkommt und gibt die gefundene Position. bzw 0 für 'nicht gefunden' zurück. }
{ First<1 heißt ab Anfang von SearchIn suchen.                                }
{ Last<1 oder Last>Length(SearchIn) heißt bis Ende von SearchIn suchen.       }
{------------------------------------------------------------------------------}
FUNCTION StrPos(const SearchFor,SearchIn:String; First,Last:NativeInt):Integer;
asm
               // RCX=@SearchFor, RDX=@SearchIn, R8=First, R9=Last
               test rcx,rcx
               jz   @Nil             // SearchFor leer
               test edx,edx
               jz   @Nil             // SearchIn leer
               dec  r8                // First 0-Based
               jge  @FirstOk         // First>=1
               xor  r8,r8
@FirstOK:     movsx r10,dword[rdx-4] // Length(SearchIn)
               test r9,r9
               jle  @LastOK          // Last<=0 heißt bis Ende suchen
               cmp  r9,r10
               jge  @LastOK          // Last>Length(SearchIn), bis Ende suchen
               mov  r10,r9            // Nur bis Last suchen
@LastOK:      movsx r9,dword[rcx-4]  // Length(SearchFor)
               push rbx              // RBX retten
               lea  rbx,[r8+r9]
               cmp  rbx,r10
               jg   @NotFound        // First>Last oder First+Length(SearchFor)>Last
               lea  rax,[rcx+r9*2-2] // @SearchFor[Length(SearchFor)]
               lea  r11,[rdx+r10*2]  // @SearchIn[Last+1]
               lea  rdx,[rdx+rbx*2-2] // @SearchIn[First+Length(SearchFor)-1]
               lea  r9,[r9*2-2]      // 2*(Length(SearchFor)-1)
               neg  r9                // -(2*(Length(SearchFor)-1))
               add  r8,r8             // 2*(First-1)
               sub  r8,rdx           // 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               mov  cx,[rax]         // Letztes Zeichen von SearchFor
               // --------------------------------------------------------------
               // RAX @SearchFor[Length(SearchFor)]
               // RDX = Aktuelle Fundstelle für letztes Zeichen von SearchFor
               // CX = Letztes Zeichen von SearchFor
               // R8  = 2*(First-1)-@SearchIn[First+Length(SearchFor)-1]
               // R9  = -(2*(Length(SearchFor)-1))
               // R11 = Terminator
@Loop:        cmp  cx,[rdx]         // Letzes Zeichen von SearchFor an EDX?
               jz   @Test0            // Ja, SearchIn auf voriges Zeichen
@AfterTest0:  cmp  cx,[rdx+2]       // Letzes Zeichen von SearchFor an EDX+1 Zeichen
               jz   @TestT           // J,
@AfterTestT:  add  rdx,8             // SearchIn+4 Zeichen
               cmp  rdx,r11           // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  rdx,-4            // SearchIn-2 Zeichen
               cmp  rdx,r11           // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NotFound        // Nicht gefunden
@Continue:    cmp  cx,[rdx-4]       // Letzes Zeichen von SearchFor an EDX-2 Zeichen?
               jz   @Test2            // Ja, SearchIn -3 Zeichen
               cmp  cx,[rdx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  rdx,2             // SearchIn + 1 Zeichen, durch folgende Adds - 2 Zeichen
@Test2:       add  rdx,-4            // SearchIn - 2 Zeichen, durch folgendes Add - 3 Zeichen
@Test0:       add  rdx,-2            // SearchIn - 1 Zeichen
@TestT:       test r9,r9             // Hat SearchFor nur 1 Zeichen?
               jz   @Found           // Ja, gefunden
               mov  r10,r9
@String:      mov  ebx,[rax+r10]    // 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+r10+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               cmp  r10,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[rax+r10+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+r10+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn+4 Zeichen
               add  r10,8             // Zeichenzahl + 4 Zeichen
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
@Found:       lea  rax,[rdx+4]      // Fundstelle
               cmp  rax,r11           // Im zu durchsuchenden Bereich?
               ja   @NotFound        // Nein, nicht gefunden.
               add  rax,r8            // Endgültige Position in Bytes
               shr  rax,1             // Endgültige Position in Zeichen
               pop  rbx              // RBX wieder herstellen
               ret
               //---------------------------------------------------------------
@Nil:         xor  eax,eax
               ret
@NotFound:    //---------------------------------------------------------------
               xor  eax,eax          // Nicht gefunden
               pop  rbx              // RBX wieder herstellen
end;
{$ENDIF}
StrPosEx

Delphi-Quellcode:
{$IFDEF CPUX86}
{------------------------------------------------------------------------------}
{ StrPosEx 32bit                                                              }
{ Sucht alle Vorkommen von SearchFor in SearchIn und stellt die Positionen    }
{ der Fundstellen in Positions.                                               }
{ Es ist Sache der aufrufenden Stelle, sicherzustellen, daas Positions lang   }
{ genug ist, alle Fundstellen zu speichern.                                   }
{ Parameter                                                                   }
{    SearchFor : String, nach dem gesucht wird.                              }
{    SearchIn  : String, in dem gesucht wird.                                }
{    Positions : Array zur Speicherung der Fundstellen.                      }
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                 0 = Keine, -1 = Alle.                                       }
{                    Bit 0 = 1 : Wenn SearchFor leer ist.                     }
{                    Bit 1 = 1 : Wenn SearchIn leer ist.                      }
{                    Bit 2 = 1 : Wenn SearchFor länger ist, als SearchIn.     }
{                    Bit 3 = 1 : Wenn Positions nicht assigned ist.           }
{                    Bit 4 = 1 : Wenn Positions zu kurz ist.                  }
{                 Wenn ein Fehler auftritt, und das korrespondierende Bit in  }
{                 Exceptions nicht gesetzt ist, werden folgende Fehlercodes   }
{                 zurückgegeben:                                              }
{                    -1 SearchFor leer.                                       }
{                    -2 SearchIn leer.                                        }
{                    -3 SearchFor länger als SearchIn.                        }
{                    -4 Positions nicht assigned.                             }
{                    -5 Positions zu kurz.                                    }
{ Wenn kein Fehler auftritt, wird die Anzahl der Fundstellen zurückgegeben.   }
{------------------------------------------------------------------------------}
FUNCTION StrPosEx(const SearchFor,SearchIn:String;
   Positions:TIntegerDynArray; Exceptions:NativeInt=0):Integer;
const
   sSearchForEmpty:String='StrPosEx:'#13'SearchFor ist leer';
   sSearchInEmpty:String='StrPosEx:'#13'SearchIn ist leer';
   sSearchForTooLong:String='StrPosEx:'#13'Searchfo ist länger als SearchIn';
   sPositionsEmpty:String='StrPosEx:'#13'Positions ist nicht assigned';
   sPositionsLength:String='StrPosEx:'#13'Das Array "Positions" ist zu kurz '+
                           'um alle Fundstellen zu speichern';
   pExClass:ExceptClass=(Exception);
asm
               // EAX=@SearchFor, EDX=@SearchIn, ECX=Positions
               // Register retten und Platz für lokale Variablen reservieren
               push ebp
               push ebx
               push edi
               push esi
               sub  esp,12            // Platz für 3 Integers
               // Prüfen, ob SearchFor und SearchIn nicht leer sind
               test eax,eax          // SearchFor leer?
               jz   @SFNil           // Ja, Fehlermedung
               test edx,edx          // SearchIn leer?
               jz   @SINil           // Ja, Fehlermeldung
               test ecx,ecx          // Positions leer?
               jz   @PosNil          // Ja, Fehlermeldung
               // Längen von SearchFor und SearchIn laden und prüfen ob
               // SearchFor nicht länger ist, als SearchIn
               mov  edi,[eax-4]      // Length(SearchFor)
               mov  ebx,[edx-4]      // Length(SearchIn)
               cmp  edi,ebx          // SearchFor länger als SearchIn?
               ja   @SFTooLong       // Ja, Fehlermeldung
               cmp  edi,1             // Hat SearchFor nur 1 Zeichen
               je   @Char            // Ja
               // Positions retten, Anzahl Fundstellen auf 0
               mov  [esp+8],ecx      // Positions
               mov  [esp+4],0         // Anzahl Fundstellen
               // Zeiger und Länge von SearchIn initialsieren
               lea  eax,[eax+edi*2-2] // EAX auf letztes Zeichen in SearchFor
               lea  ebp,[edx+ebx*2]  // EBP hinter letztes Zeichen von SearchIn
               lea  edx,[edx+edi*2-2] // EDX auf Ende der ersten potentiellen Fundstelle
               lea  edi,[edi*2-2]    // EDI = 2*(Length(SearchFor)-1)
               neg  edi              // EDI = -(2*(Length(SearchFor)-1))
               xor  ecx,ecx
               sub  ecx,edx          // ECX = -SearchIn[Length(SearchFor)-1]
               mov  [esp],ecx        // Für spätere Positionsberechnung
               // --------------------------------------------------------------
               // EAX    = Zeigt auf letztes Zeichen in SearchFor
               // EDX    = Zeigt auf Ende der nächsten potentiellen Fundstelle
               // EBP    = Zeigt hinter letztes Zeichen von SearchIn
               // EDI    = -(2*(Length(SearchFor)-1))
               // [ESP-8] = Postions
               // [ESP-4] = Anzahl Fundstellen
               // [ESP]  = -SearchIn[Length(SearchFor)-1]
@Start:       mov  cx,[eax]         // letztes Zeichen von SearchFor
@Loop:        cmp  cx,[edx]         // CX an [EDX]?
               jz   @Test0            // Ja, weitere Zeichen ab [EDX-1 Char] prüfen
@AfterTest0:  cmp  cx,[edx+2]       // CX an [EDX + 1 Char]?
               jz   @TestT           // Ja, weitere Zeichen ab [EDX] prüfen
@AfterTestT:  add  edx,8             // SearchIn + 4 Chars
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  edx,-4            // SearchIn - 2 Chars
               cmp  edx,ebp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NoFurther       // Keine weiteren Fundstellen
@Continue:    cmp  cx,[edx-4]       // CX an [EDX - 2 Chars]?
               jz   @Test2            // Ja, SearchIn - 3 Chars
               cmp  cx,[edx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  edx,2             // SearchIn + 1 Char, durch folgende Adds - 2 Chars
@Test2:       add  edx,-4            // SearchIn - 2 Chars, durch folgendes Add - 3 Chars
@Test0:       add  edx,-2            // SearchIn - 1 Char
@TestT:       test edi,edi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
               mov  esi,edi          // -(2*(Length(SearchFor)-1))
@String:      mov  ebx,[eax+esi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               cmp  esi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[eax+esi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[edx+esi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               add  esi,8             // Zeichenzahl + 4 Chars
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
               // Gefunden. EDX zeigt auf Fundstelle - 1 Zeichen
@Found:       lea  ecx,[edx+4]      // Fundstelle + 1 Zeichen
               cmp  ecx,ebp          // Im zu durchsuchenden Bereich?
               ja   @NoFurther       // Nein, keine weiteren Fundstellen
               add  ecx,[esp]        // Position in Bytes
               shr  ecx,1             // Position in Zeichen
               mov  esi,[esp+8]      // Positions
               mov  ebx,[esp+4]      // Bisherige Anzahl Fundstellen
               cmp  ebx,[esi-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               mov  [esi+ebx*4],ecx  // Fundstelle speichern
               inc  ebx              // Anzahl Fundstellen + 1
               mov  [esp+4],ebx      // Anzahl Fundstellen speichern
               // EDX auf nächste potentielle Fundstelle
               mov  esi,edi          // -(2*(Length(SearchFor)-1))
               neg  esi              //   2*(Length(SearchFor)-1)
               lea  edx,[edx+esi+4]  // EDX=nächste potentielle Fundstelle
               cmp  edx,ebp          // Noch im gültigen Bereich?
               jb   @Start           // Ja, weiter suchen
               jmp  @NoFurther       // Nein, keine weiteren Fundstellen
               // --------------------------------------------------------------
               // Suche nach nur einem Zeichen
               // EAX = SearchFor
               // EDX = SearchIn
               // ECX = Positions
               // EBX = Lenght(SearchIn)
@Char:        mov  ebp,ecx          // Positions
               xor  ecx,ecx          // Anzahl Fundstellen
               lea  edx,[edx+ebx*2]  // Hinter letztes Zeichen von SearchIn
               lea  edi,[ebx+1]      // für spötere Positionsermittlung
               neg  ebx
               mov  ax,[eax]         // gesuchtes Zeichen
@CharLoop:    cmp  ax,[edx+ebx*2]   // AX an aktueller Position?
               jz   @CharFound1       // Ja
               cmp  ax,[edx+ebx*2+2] // AX an nächster Position?
               jz   @CharFound2       // Ja
               add  ebx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               // An [EDX+EBX*2] gefunden
@CharFound1:  cmp  ecx,[ebp-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  esi,[edi+ebx]    // Fundstelle
               mov  [ebp+ecx*4],esi  // In Positions speichern
               inc  ecx              // Anzahl Fundstellen
               inc  ebx              // 1 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               // An [EDX+EBX*2+2] gefunden
@CharFound2:  cmp  ecx,[ebp-4]      // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  esi,[edi+ebx+1]  // Fundstelle * 2
               mov  [ebp+ecx*4],esi  // In Positions speichern
               inc  ecx              // Anzahl Fundstellen
               add  ebx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurtherC      // Keine weiteren Fundstellen
               //---------------------------------------------------------------
               // SearchFor ist leer
@SFNil:       mov  eax,sSearchForEmpty
               mov  ecx,1             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchIn ist leer
@SINil:       mov  eax,sSearchInEmpty
               mov  ecx,2             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchFor ist länger als SearchIn
@SFTooLong:   mov  eax,sSearchForTooLong
               mov  ecx,3             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions ist nicht assigned
@PosNil:      mov  eax,sPositionsEmpty
               mov  ecx,4             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions nicht lang genug um neue Fundstelle zu speichern
@OutOfMem:    mov  eax,sPositionsLength
               mov  ecx,5             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Keine weiteren Fundstellen
@NoFurther:   mov  ecx,[esp+4]      // Anzahl Fundstellen
@NoFurtherC:  xor  eax,eax          // ´Kein Fehler
               //---------------------------------------------------------------
               // Stack bereinigen und Register restaurieren
@End:         add  esp,12            // Stack bereinigen
               pop  esi              // ESI wieder herstellen
               pop  edi              // EDI wieder herstellen
               pop  ebx              // EBX wieder herstellen
               pop  ebp              // EBP wieder herstellen
               //---------------------------------------------------------------
               // Prüfen ob Fehler vorliegt und ggfs. Exception auslösen
               test eax,eax          // Fehler ?
               jz   @NoError         // Nein, Anzahl Fundstellen zurückgeben
               mov  edx,1
               shl  edx,cl
               neg  ecx              // Fehlercode
               shr  edx,1
               test [ebp+8],edx      // Exception werfen?
               jz   @NoError         // Nein, nur Fehlercode zurückgeben
               mov  ecx,eax          // Fehlertext
               mov  eax,pExClass     // InstanceOrVMT
               mov  edx,1             // Alloc
               call Exception.Create
               call System.@RaiseExcept
               //---------------------------------------------------------------
               // Anzahl Fundstellen bzw. Fehlercode in Result
@NoError:     mov  eax,ecx          // Anzahl Fundstellen bzw. FehlerCode
end;
{$ENDIF}
Delphi-Quellcode:
{$IFDEF CPUX64}
{------------------------------------------------------------------------------}
{ StrPosEx 64bit                                                              }
{ Sucht alle Vorkommen von SearchFor in SearchIn und stellt die Positionen    }
{ der Fundstellen in Positions.                                               }
{ Es ist Sache der aufrufenden Stelle, sicherzustellen, daas Positions lang   }
{ genug ist, alle Fundstellen zu speichern.                                   }
{ Parameter                                                                   }
{    SearchFor : String, nach dem gesucht wird.                              }
{    SearchIn  : String, inem gesucht wird.                                  }
{    Positions : Array zur Speicherung der Fundstellen.                      }
{    Exceptions : Gibt an, in welchen Fällen Exceptions ausgelöst werden.     }
{                 0 = Keine, -1 = Alle                                        }
{                 In der aktuellen Version ist das Werfen von Exceptions      }
{                 deaktiviert, weil der Code hier nicht funktioniert.         }
{                 In anderen Anwendungen funktioniert er.                     }
{                    Bit 0 = 1 : Wenn SearchFor leer ist.                     }
{                    Bit 1 = 1 : Wenn SearchIn leer ist.                      }
{                    Bit 2 = 1 : Wenn SearchFor länger ist, als SearchIn.     }
{                    Bit 3 = 1 : Wenn Positions nicht assigned ist.           }
{                    Bit 4 = 1 : Wenn Positions zu kurz ist.                  }
{                 Wenn ein Fehler auftritt, und das korrespondierende Bit in  }
{                 Exceptions nicht gesetzt ist, werden folgende Fehlercodes   }
{                 zurückgegeben:                                              }
{                    -1 SearchFor leer.                                       }
{                    -2 SearchIn leer.                                        }
{                    -3 SearchFor länger als SearchIn.                        }
{                    -4 Positions nicht assigned.                             }
{                    -5 Positions zu kurz.                                    }
{ Wenn kein Fehler auftritt, wird die Anzahl der Fundstellen zurückgegeben.   }
{------------------------------------------------------------------------------}
FUNCTION StrPosEx(const SearchFor,SearchIn:String;
   Positions:TIntegerDynArray; Exceptions:NativeInt=0):Integer;
const
   sSearchForEmpty:String='StrPosEx:'#13'SearchFor ist leer';
   sSearchInEmpty:String='StrPosEx:'#13'SearchIn ist leer';
   sSearchForTooLong:String='StrPosEx:'#13'Searchfo ist länger als SearchIn';
   sPositionsEmpty:String='StrPosEx:'#13'Positions ist nicht assigned';
   sPositionsLength:String='StrPosEx:'#13'Das Array "Positions" ist zu kurz '+
                           'um alle Fundstellen zu speichern';
   pExClass:ExceptClass=(Exception);
asm
               // RCX=@SearchFor, RDX=@SearchIn, R8=Positions, R9=Exceptions
               // Exceptions = 0 (keine Exceptions) setzen, weil das hier
               // nicht funktioniert (In anderen Projektion funktionierts).
               xor  r9,r9             // Keine Exceptions
               // Register retten
               push rbp
               push rbx
               push rdi
               push rsi
               push r12
               // Prüfen, ob SearchFor und SearchIn nicht leer sind
               test rcx,rcx          // SearchFor leer?
               jz   @SFNil           // Ja, Fehlermedung
               test rdx,rdx          // SearchIn leer?
               jz   @SINil           // Ja, Fehlermeldung
               test r8,r8             // Positions assigned?
               jz   @PosEmpty        // Nein, Fehlermeldung
               // Längen von Positons, SearchFor und SearchIn laden und
               // prüfen ob SearchFor nicht länger ist, als SearchIn
               movsx rdi,dword[rcx-4] // Length(SearchFor)
               movsx rbx,dword[rdx-4] // Length(SearchIn)
               cmp  edi,ebx          // SearchFor länger als SearchIn?
               ja   @SFTooLong       // Ja, Fehlermeldung
               mov  r11,[r8-8]       // Length Positions
               xor  r10,r10           // Anzahl Fundstellen auf 0
               cmp  edi,1             // Hat SearchFor nur 1 Zeichen
               je   @Char            // Ja
               // Zeiger und Länge von SearchIn initialsieren
               lea  rax,[rcx+rdi*2-2] // RAX auf letztes Zeichen in SearchFor
               lea  rbp,[rdx+rbx*2]  // RBP hinter letztes Zeichen von SearchIn
               lea  rdx,[rdx+rdi*2-2] // RDX auf Ende der ersten potentiellen Fundstelle
               lea  rdi,[rdi*2-2]    // RDI = 2*(Length(SearchFor)-1)
               neg  rdi              // RDI = -(2*(Length(SearchFor)-1))
               xor  r12,r12
               sub  r12,rdx          // R12 = -SearchIn[Length(SearchFor)-1]
               // --------------------------------------------------------------
               // RAX    = Zeigt auf letztes Zeichen in SearchFor
               // RDX    = Zeigt auf Ende der nächsten potentiellen Fundstelle
               // RBP    = Zeigt hinter letztes Zeichen von SearchIn
               // RDI    = -(2*(Length(SearchFor)-1))
               // R8      = Postions
               // R9      = Exceptions
               // R10     = Anzahl Fundstellen
               // R11     = Length(Positions)
               // R12     = -SearchIn[Length(SearchFor)-1]
               //           Für Ermittlung der Positionen von Fundstellen
@Start:       mov  cx,[rax]         // letztes Zeichen von SearchFor
@Loop:        cmp  cx,[rdx]         // CX an [EDX]?
               jz   @Test0            // Ja, weitere Zeichen ab [EDX-1 Char] prüfen
@AfterTest0:  cmp  cx,[rdx+2]       // CX an [EDX + 1 Char]?
               jz   @TestT           // Ja, weitere Zeichen ab [EDX] prüfen
@AfterTestT:  add  rdx,8             // SearchIn + 4 Chars
               cmp  rdx,rbp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Continue        // Ja
@EndLoop:     add  rdx,-4            // SearchIn - 2 Chars
               cmp  rdx,rbp          // SearchIn noch im zu durchsuchenden Bereich
               jb   @Loop            // Ja
               jmp  @NoFurther       // Keine weiteren Fundstellen
@Continue:    cmp  cx,[rdx-4]       // CX an [EDX - 2 Chars]?
               jz   @Test2            // Ja, SearchIn - 3 Chars
               cmp  cx,[rdx-2]       // Letzes Zeichen von SearchFor an EDX-1 Zeichen?
               jnz  @Loop            // Nein, nächste Position prüfen
@Test1:       add  rdx,2             // SearchIn + 1 Char, durch folgende Adds - 2 Chars
@Test2:       add  rdx,-4            // SearchIn - 2 Chars, durch folgendes Add - 3 Chars
@Test0:       add  rdx,-2            // SearchIn - 1 Char
@TestT:       test rdi,rdi          // Alle Zeichen von SearchFor gefunden?
               jz   @Found           // Ja, gefunden
               mov  rsi,rdi          // -(2*(Length(SearchFor)-1))
@String:      mov  ebx,[rax+rsi]    // 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+rsi+2]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               cmp  rsi,-4            // Alle Zeichen gefunden?
               jge  @Found           // Ja
               mov  ebx,[rax+rsi+4]  // Nächste 2 Zeichen aus SearchFor
               cmp  ebx,[rdx+rsi+6]  // In SearchIn?
               jnz  @AfterTestT      // Nein, SearchIn + 4 Chars
               add  rsi,8             // Zeichenzahl + 4 Chars
               jl   @String          // Nächste 4 Zeichen prüfen
               //---------------------------------------------------------------
               // Gefunden. RDX zeigt auf Fundstelle - 1 Zeichen
@Found:       lea  rsi,[rdx+4]      // Fundstelle + 1 Zeichen
               cmp  rsi,rbp          // Im zu durchsuchenden Bereich?
               ja   @NoFurther       // Nein, keine weiteren Fundstellen
               add  rsi,r12           // Position in Bytes
               shr  rsi,1             // Position in Zeichen
               cmp  r10,r11           // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               mov  [r8+r10*4],esi   // Fundstelle speichern
               inc  r10               // Anzahl Fundstellen + 1
               // RDX auf nächste potentielle Fundstelle
               mov  rsi,rdi          // -(2*(Length(SearchFor)-1))
               neg  rsi              //   2*(Length(SearchFor)-1)
               lea  rdx,[rdx+rsi+4]  // EDX=nächste potentielle Fundstelle
               cmp  rdx,rbp          // Noch im gültigen Bereich?
               jb   @Loop            // Ja, weiter suchen
               jmp  @NoFurther       // Nein, keine weiteren Fundstellen
               // --------------------------------------------------------------
               // Suche nach nur einem Zeichen
               // RCX = SearchFor
               // RDX = SearchIn
               // RBX = Lenght(SearchIn)
               // R8  = Positions
               // R9  = Exceptions
               // R10 = Anzahl Fundstellen
               // R11 = Length(Positions)
@Char:        lea  rdx,[rdx+rbx*2]  // Hinter letztes Zeichen von SearchIn
               lea  rdi,[rbx+1]      // für spätere Positionsermittlung
               neg  rbx
               mov  ax,[rcx]         // gesuchtes Zeichen
@CharLoop:    cmp  ax,[rdx+rbx*2]   // AX an aktueller Position?
               jz   @CharFound1       // Ja
               cmp  ax,[rdx+rbx*2+2] // AX an nächster Position?
               jz   @CharFound2       // Ja
               add  rbx,2             // 2 Zeichen weiter
               jl   @CharLoop
               jmp  @NoFurther       // Keine weiteren Fundstellen
               // An [EDX+EBX*2] gefunden
@CharFound1:  cmp  r10,r11           // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  rsi,[rdi+rbx]    // Fundstelle
               mov  [r8+r10*4],esi   // In Positions speichern
               inc  r10               // Anzahl Fundstellen
               inc  rbx              // 1 Zeichen weiter
               jl   @CharLoop        // Weiter, solange negativ
               jmp  @NoFurther       // Keine weiteren Fundstellen
               // An [EDX+EBX*2+2] gefunden
@CharFound2:  cmp  r10,r11           // Noch Platz in Positions?
               jae  @OutOfMem        // Nein
               lea  rsi,[rdi+rbx+1]  // Fundstelle
               mov  [r8+r10*4],esi   // In Positions speichern
               inc  r10               // Anzahl Fundstellen
               add  rbx,2             // 2 Zeichen weiter
               jl   @CharLoop        // Weiter, solange negativ
               jmp  @NoFurther       // Keine weiteren Fundstellen
               //---------------------------------------------------------------
               // SearchFor ist leer
@SFNil:       mov  r8,sSearchForEmpty
               mov  ecx,1             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchIn ist leer
@SINil:       mov  r8,sSearchInEmpty
               mov  ecx,2             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // SearchFor ist länger als SearchIn
@SFTooLong:   mov  r8,sSearchForTooLong
               mov  ecx,3             // Fehlercode
               jmp  @End
               //---------------------------------------------------------------
               // Positions ist nicht assigned
@PosEmpty:    mov  r8,sPositionsEmpty
               mov  ecx,4             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Positions nicht lang genug um neue Fundstelle zu speichern
@OutOfMem:    mov  r8,sPositionsLength
               mov  ecx,5             // Fehlercode
               jmp  @End             // Fehlermeldung
               //---------------------------------------------------------------
               // Keine weiteren Fundstellen
@NoFurther:   xor  r8,r8             // ´Kein Fehler
               mov  rcx,r10           // Anzahl Fundstellen
               //---------------------------------------------------------------
               // Register restaurieren
@End:         pop  r12               // R12 wieder herstellen
               pop  rsi              // RSI wieder herstellen
               pop  rdi              // RDI wieder herstellen
               pop  rbx              // RBX wieder herstellen
               pop  rbp              // RBP wieder herstellen
               //---------------------------------------------------------------
               // Prüfen ob Fehler vorliegt und ggfs. Exception auslösen
               test r8,r8             // Fehler ?
               jz   @NoError         // Nein, Anzahl Fundstellen zurückgeben
               mov  edx,1
               shl  edx,cl
               neg  ecx              // Fehlercode
               shr  edx,1
               test r9d,edx          // Exception werfen?
               jz   @NoError         // Nein, nur Fehlercode zurückgeben
               // Exception auslösen
               push rcx              // Fehlercode retten
               push rbp
               sub  rsp,$20
               mov  rbp,rsp
               mov  rcx,pExClass     // InstanceOrVMT
               mov  dl,$01            // Alloc
               call Exception.Create
               mov  rcx,rax
               call System.@RaiseExcept
               add  rsp,$20
               pop  rbp
               pop  rcx              // Fehlercode
               //---------------------------------------------------------------
               // Anzahl Fundstellen bzw. Fehlercode in Result
@NoError:     mov  eax,ecx          // Anzahl Fundstellen bzw. FehlerCode
end;
{$ENDIF}

Stevie 27. Nov 2024 18:59

AW: Alternative zu PosEx
 
Übrigens ist StrPos nicht schneller als das, was wir in der RTL haben - im Rahmen von https://quality.embarcadero.com/browse/RSP-13687 hatte ich die ursprüngliche Implementierung in asm für x86 von Aleksandr Sharahov (die auch deiner Implementierung zugrunde liegt) in pure pascal reimplementiert, welche nun somit auch gleich auf alle Plattformen portierbar ist. Ja, der Code steckt voller gotos, aber besser bekommt man es nicht ohne asm hin.

Einzig dein neuer 4. Parameter, um den Suchbereich zu limitieren, ist interessant und ggf eine Überlegung wert, diesen in der RTL auch unterzubringen.
Sollte sogar ziemlich einfach zu implementieren sein, da dieser der Länge des zu durchsuchenden Strings entspricht, die intern sowieso ermittelt wird.

Sinspin 27. Nov 2024 19:59

AW: Alternative zu PosEx
 
Ich habe auch schon die Erfahrung gemacht das FPC/Delphi schnelleren Code erzeugt hat als selbst geschriebener Assembler.
Habe Stunden über Stunden investiert um meinen code besser zu bekommen. Nur um ihn dann für 64Bit in die Tonne zu hauen und zu sehen, er geht auch ohne Assembler schnell genug.

Blup 28. Nov 2024 09:04

AW: Alternative zu PosEx
 
Zitat:

Zitat von Amateurprofi (Beitrag 1543557)
Und wo ist da der Fehler?
"PaPa" wird in "PaPaPa" an Position 1 gefunden.
Dann wird geprüft, ob ab hinter der Fundstelle, also ab Position 1+Length("PaPa") der Text "PaPa" noch einmal gefunden wird.
Das ist nicht der Fall, also wird korrekt 1 zurückgegeben.

Das ist Ansichtssache. Bei einer Funktion die Sucht und Ersetzt wäre dies auch sicherlich richtig.
Bei einer Funktion die alle Fundstellen zurückgibt, erwarte ich alle, auch wenn die gefundenen Zeichenketten sich überschneiden.
Welche davon ignoriert werden, kann man beim Verarbeiten des Ergebnis der Suche entscheiden.

Blup 28. Nov 2024 09:22

AW: Alternative zu PosEx
 
Zitat:

Zitat von himitsu (Beitrag 1543558)
Per se ist OUT hier falsch, jedenfalls in Bezug auf Managed-Types, wie z.B. dynamische Arrays.
Zum Glück macht Delphi hier heimlich, und ohne was zu sagen, ein VAR daraus.

Bei OUT ist es möglich auch die Referenz zu ändern, was hier "eigentlich" zu einem Speicherleck führen würde, wenn vor dem Aufruf das Array einen Inhalt hätte.

Da Managed-Types immer initialisisert sind, wäre OUT nach deiner Auffassung für diese immer falsch.
OUT intern für Managed-Types als VAR zu behandeln macht Delphi schon richtig, beide übergeben einen Zeiger auf die Variable.

Was meiner Meinung nach fehlt ist eine Warnung auch für Managed-Types.
- VAR-Parameter: Warnung wenn vorher keine Zuweisung auf die Variable erfolgt (kann auch leer sein, aber automatische Initialisierung zählt nicht)
- OUT-Parameter: Warnung wenn vorher eine Zuweisung auf die Variable erfolgt, diese Variable aber nicht mehr vor dem Aufruf als OUT-Parameter ausgewertet wird.

Maekkelrajter 28. Nov 2024 10:20

AW: Alternative zu PosEx
 
Zitat:

Zitat von Sinspin (Beitrag 1543605)
Ich habe auch schon die Erfahrung gemacht das FPC/Delphi schnelleren Code erzeugt hat als selbst geschriebener Assembler.

Das ging mir ähnlich. Aus 'maschinen-nahem' Pascal mit Delphi 4 compilierter Code war schneller als meine selbstgeschiebenen Assembler-Routinen, die unter TP6 bzw. BP7 noch für einen ordentlichen Performance-Schub gesorgt hatten.

Zitat:

Zitat von Stevie (Beitrag 1543604)
Einzig dein neuer 4. Parameter, um den Suchbereich zu limitieren, ist interessant und ggf eine Überlegung wert, diesen in der RTL auch unterzubringen.
Sollte sogar ziemlich einfach zu implementieren sein, da dieser der Länge des zu durchsuchenden Strings entspricht, die intern sowieso ermittelt wird.

Hier hatte ich mich vor ein paar Jahren schon mit dem Thema befasst und eine Variante von Pos() mit 4 Parametern entwickelt.

Gruß LP

Stevie 28. Nov 2024 10:42

AW: Alternative zu PosEx
 
Ich hab mir mal System._UStrPos in Delphi 12 angeschaut und wenn ich nicht komplett auf dem Holzweg bin, dann ist die Erweiterung mit einigen wenigen Anpassungen erledigt. Defaultwert von Count ist MaxInt

Hier mal der relevante Ausschnitt
Delphi-Quellcode:
begin
-  if (Str = nil) or (SubStr = nil) or (Offset < 1) then
+  if (Str = nil) or (SubStr = nil) or (Offset < 1) or (Count < 1) then
    goto Exit0;

  // fast access to length - did the nil check already
  lenSub := PInteger(SubStr)[-1];
  Dec(lenSub);
  len := PInteger(Str)[-1];
+  Dec(Offset);
+  Cardinal(Count) := Cardinal(Count) + Cardinal(Offset);
+  if Cardinal(len) > Cardinal(Count) then len := Count;
-  if (len < lenSub + Offset) then
+  if (len <= lenSub + Offset) then
    goto Exit0;

  Stop := @Str[len];
  Str := @Str[lenSub];
  SubStr := @SubStr[lenSub];
  Start := Str;
-  Str := @Str[Offset + 3];
+  Str := @Str[Offset + 4];

Blup 28. Nov 2024 15:02

AW: Alternative zu PosEx
 
Zum Vergleich mit Pos() und SetLength() für das Ergebnis:
Delphi-Quellcode:
function MyStrPosEx(const SearchFor, SearchIn: string): TIntegerDynArray;

  function Search(var Index: Integer): Boolean;
  begin
    Index := Pos(SearchFor, SearchIn, Index + 1);
    Result := (Index > 0);
  end;

begin
  var Count: Integer := 0;
  var Index: Integer := 0;
  while Search(Index) do
  begin
    Inc(Count);
    {Array vergrößern braucht viel Zeit, deshalb gleich etwas mehr Platz reservieren}
    if Length(Result) < Count then
      SetLength(Result, Count * 4);

    Result[Count - 1] := Index;
  end;
  SetLength(Result, Count);
end;
Code:
sRandomString := RandomString(MaxInt div 8 - 6) + 'PaPaPa'
0:00:01.327

SetLength(Positions, 3)
StrPosEx('PaPa', 'sRandomString', Positions)
0:00:00.081
Result = 1
Positions = [268435450,0,0]

Result := MyStrPosEx('PaPa', 'sRandomString')
0:00:00.075
Result = [268435450,268435452]

Amateurprofi 29. Nov 2024 00:40

AW: Alternative zu PosEx
 
Zitat:

Zitat von Blup (Beitrag 1543614)
Zitat:

Zitat von Amateurprofi (Beitrag 1543557)
Und wo ist da der Fehler?
"PaPa" wird in "PaPaPa" an Position 1 gefunden.
Dann wird geprüft, ob ab hinter der Fundstelle, also ab Position 1+Length("PaPa") der Text "PaPa" noch einmal gefunden wird.
Das ist nicht der Fall, also wird korrekt 1 zurückgegeben.

Das ist Ansichtssache. Bei einer Funktion die Sucht und Ersetzt wäre dies auch sicherlich richtig.
Bei einer Funktion die alle Fundstellen zurückgibt, erwarte ich alle, auch wenn die gefundenen Zeichenketten sich überschneiden.
Welche davon ignoriert werden, kann man beim Verarbeiten des Ergebnis der Suche entscheiden.

Zu "Bei einer Funktion die Sucht und Ersetzt wäre dies auch sicherlich richtig."
Ja, und meine Ansicht ist, dass eine Suche genau die Fundstellen finden soll, die im Zweifelsfall auch ersetzt würden.

Aber, wie du sagtest: Ist Ansichtssache.
Ich bin zum Beispiel der Ansicht, dass 2 * 2 = 5 ergeben sollte, wenn in 2024 der 31te eines Monats auf einen Montag fällt.

Amateurprofi 29. Nov 2024 01:10

AW: Alternative zu PosEx
 
Zitat:

Zitat von Blup (Beitrag 1543644)
Zum Vergleich mit Pos() und SetLength() für das Ergebnis:
Delphi-Quellcode:
function MyStrPosEx(const SearchFor, SearchIn: string): TIntegerDynArray;

  function Search(var Index: Integer): Boolean;
  begin
    Index := Pos(SearchFor, SearchIn, Index + 1);
    Result := (Index > 0);
  end;

begin
  var Count: Integer := 0;
  var Index: Integer := 0;
  while Search(Index) do
  begin
    Inc(Count);
    {Array vergrößern braucht viel Zeit, deshalb gleich etwas mehr Platz reservieren}
    if Length(Result) < Count then
      SetLength(Result, Count * 4);

    Result[Count - 1] := Index;
  end;
  SetLength(Result, Count);
end;
Code:
sRandomString := RandomString(MaxInt div 8 - 6) + 'PaPaPa'
0:00:01.327

SetLength(Positions, 3)
StrPosEx('PaPa', 'sRandomString', Positions)
0:00:00.081
Result = 1
Positions = [268435450,0,0]

Result := MyStrPosEx('PaPa', 'sRandomString')
0:00:00.075
Result = [268435450,268435452]

Und jetzt das Ganze noch einmal, wenn der zu durchsuchende Text 80 Mio Zeichen hat, nur aus "0" .. "9" besteht und Du die Fundstellen für die "7" haben möchtest.

Blup 29. Nov 2024 10:11

AW: Alternative zu PosEx
 
Ich habe den Faktor bei "SetLength(Result, Count * 2)" auf 2 geändert. Das bringt noch ein par Millisekunden.
Delphi-Quellcode:
function MyStrPosEx(const SearchFor, SearchIn: string): TIntegerDynArray;

  function Search(var Index: Integer): Boolean;
  begin
    Index := Pos(SearchFor, SearchIn, Index + 1);
    Result := (Index > 0);
  end;

begin
  var Count: Integer := 0;
  var Index: Integer := 0;
  while Search(Index) do
  begin
    Inc(Count);
    {Array vergrößern braucht viel Zeit, deshalb gleich etwas mehr Platz reservieren}
    if Length(Result) < Count then
      SetLength(Result, Count * 2);

    Result[Count - 1] := Index;
  end;
  SetLength(Result, Count);
end;
Trotzdem ist natürlich jedes "SetLength()" potentiell mit dem Umkopieren des Inhalts verbunden und benötigt Zeit.
Wenn man die Menge der Ergebnisse abschätzen kann, ist es sinnvoll das Array gleich in der entsprechenden Größe zu reservieren und zum Schluss zu kürzen.
Code:
sRandomString := RandomString(8000000)
0:00:00.402

SetLength(Positions, 10000000)
StrPosEx('7', 'sRandomString', Positions)
0:00:00.102
Result = 7999774
Positions = [15,19,45,63,98,113,122,127,132,133, ... ,0,0,0,0,0,0,0,0,0,0]

Result := MyStrPosEx('7', 'sRandomString')
0:00:00.217
Length = 7999774
Result = [15,19,45,63,98,113,122,127,132,133, ... ,79999929,79999936,79999958,79999963,79999969,79999970,79999975,79999976,79999990,79999995]
Die Unterschiede im Ergebnis liegen hier daran, dass "777" bei mir zwei Fundstellen bedeuten:
Code:
sRandomString := RandomString(8000000)
0:00:00.407

SetLength(Positions, 1000000)
StrPosEx('77', 'sRandomString', Positions)
0:00:00.091
Result = 727065
Positions = [322,351,413,526,563,800,807,828,854,1113, ... ,0,0,0,0,0,0,0,0,0,0]

Result := MyStrPosEx('77', 'sRandomString')
0:00:00.108
Length = 799271
Result = [322,351,352,413,526,563,800,807,828,854, ... ,79999371,79999406,79999515,79999730,79999819,79999827,79999843,79999868,79999931,79999954]

himitsu 29. Nov 2024 10:55

AW: Alternative zu PosEx
 
Wenn es möglich ist, das Resize "inplace" zu machen, dann geht es schnell.
Wenn dahinter noch freier Speicher liegt und dieser genutzt werden kann ...


Bzw. vorher bereits mehr/genug reservieren, anstatt mittendrin immer wieder neu.

Blup 29. Nov 2024 11:50

AW: Alternative zu PosEx
 
Das anfängliche "SetLength()" bewirkt weniger als erwartet, dafür bingt "inline" deutlich mehr Zeitersparnis:
Delphi-Quellcode:
function MyStrPosEx(const SearchFor, SearchIn: string; Estimated: Integer = 0): TIntegerDynArray;

  function Search(const SearchFor, SearchIn: string; var Index: Integer): Boolean; inline;
  begin
    Index := Pos(SearchFor, SearchIn, Index + 1);
    Result := (Index > 0);
  end;

begin
  SetLength(Result, Estimated);
  var Count: Integer := 0;
  var Index: Integer := 0;
  while Search(SearchFor, SearchIn, Index) do
  begin
    Inc(Count);
    {Array vergrößern braucht viel Zeit, deshalb gleich etwas mehr Platz reservieren}
    if Estimated < Count then
    begin
      Estimated := Count * 2;
      SetLength(Result, Estimated);
    end;
    Result[Count - 1] := Index;
  end;
  SetLength(Result, Count);
end;
Code:
StrPosEx('7', sRandomString, Positions)
0:00:00.102

Result := MyStrPosEx('7', sRandomString, 0)
0:00:00.168

Result := MyStrPosEx('7', sRandomString, 10000000)
0:00:00.157

Blup 30. Nov 2024 12:48

AW: Alternative zu PosEx
 
Hab mich überzeugen lassen, "MyStrPosEx('77', '777')" liefert jetzt nur noch eine Fundstelle an Position 1.
Die Ergebnisse stimmen mit StrPosEx('77', '777', Positions) überein.
Delphi-Quellcode:
function MyStrPosEx(const SearchFor, SearchIn: string; Estimated: Integer = 0): TIntegerDynArray;

  function Search(const SearchFor, SearchIn: string; var Index: Integer): Boolean; inline;
  begin
    Index := Pos(SearchFor, SearchIn, Index);
    Result := (Index > 0);
  end;

begin
  SetLength(Result, Estimated);
  var Count: Integer := 0;
  var Index: Integer := 1;
  var SearchForLength := Length(SearchFor);
  while Search(SearchFor, SearchIn, Index) do
  begin
    Inc(Count);
    if Estimated < Count then
    begin
      Estimated := Count * 2;
      SetLength(Result, Estimated);
    end;
    Result[Count - 1] := Index;
    Inc(Index, SearchForLength);
  end;
  SetLength(Result, Count);
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:04 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz