![]() |
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} |
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:
:duck:
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; |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
War Pos nicht früher (vor Äonen) mal langsamer?
Ich glaub dort wurde auch was vom FastStrings mal mit übernommen. |
AW: Alternative zu PosEx
Zitat:
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." |
AW: Alternative zu PosEx
Zitat:
|
AW: Alternative zu PosEx
Zitat:
|
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:)
|
AW: Alternative zu PosEx
Zitat:
Das Prinzip nennt sich bei Boyer-Moore die "Bad-Character-Heuristik", siehe ![]() |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
|
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:
StrPosEx
{$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}
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} |
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:
Was meinst du damit? Fehlererkennung geht nicht oder Exception auswerfen geht nicht in 64Bit?
{ 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. } |
AW: Alternative zu PosEx
Zitat:
"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. |
AW: Alternative zu PosEx
Nene :
Delphi-Quellcode:
Ist aber eigentlich wurscht. Die meißten Exceptions enstehen eh durch unsaubere Programmierung. Sind also vermeidbar.
{
Exceptions ... deaktiviert, weil der Code hier nicht funktioniert. } 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. |
AW: Alternative zu PosEx
64bit StrPos ist defekt.
Teste selbst:
Delphi-Quellcode:
liefert 12 und nicht 7
StrPos('world', 'hello world', 0, 11)
|
AW: Alternative zu PosEx
Ich habe StrPosEx (32Bit) getestet und einen Fehler gefunden:
Code:
Man muss das Array vor dem Aufruf auf die Anzahl der Ergebnisse anpassen.
SetLength(Positions, 3)
StrPosEx('PaPa', 'PaPaPa', Positions) Result = 1 Positions = [1,0,0] 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; |
AW: Alternative zu PosEx
Zitat:
"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? |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
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 --------------------------- |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
Delphi 11U2, Win11pro, Intel i7-11800H . |
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. |
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.
|
AW: Alternative zu PosEx
Zitat:
Stevie schieb 64bit und ich teste 32bit. Bei 64bit auch bei mir 12 statt 7. Werde den Fehler suchen und korrigieren. |
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:
StrPosEx
{$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}
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} |
AW: Alternative zu PosEx
Übrigens ist StrPos nicht schneller als das, was wir in der RTL haben - im Rahmen von
![]() 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. |
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. |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
Zitat:
![]() Gruß LP |
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]; |
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] |
AW: Alternative zu PosEx
Zitat:
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. |
AW: Alternative zu PosEx
Zitat:
|
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:
Trotzdem ist natürlich jedes "SetLength()" potentiell mit dem Umkopieren des Inhalts verbunden und benötigt Zeit.
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; 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:
Die Unterschiede im Ergebnis liegen hier daran, dass "777" bei mir zwei Fundstellen bedeuten:
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]
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] |
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. |
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 |
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. |
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