FindLastString() »»» Letztes Vorkommen von Strings ermitteln

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Phase?

Das wird aber eine Lange Phase. Von wo bis wo denn? PB 4.10 bis PB 99.99? :lol:

Da spiel ich lieber Phase 10... :wink:
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
al90
Beiträge: 1101
Registriert: 06.01.2005 23:15
Kontaktdaten:

Beitrag von al90 »

AND51 hat geschrieben:Phase?

Das wird aber eine Lange Phase. Von wo bis wo denn? PB 4.10 bis PB 99.99? :lol:

Da spiel ich lieber Phase 10... :wink:
Ich meinte damit die Phase während Du den Befehl eintippst. Umso schneller
Du bist, desto weniger musste schwitzen. :lol:
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Ich mach mir nen Macro, das den Befehl umschreibt.


Aber halt, das geht ja gar nicht... Während ich an dem Wort hinter "Macro" vorbei muss, trifft mich garantiert der Kollaps! :lol:
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Beitrag von hjbremer »

Hallo, viele gute Lösungen, einige schnell, andere langsam und fast alle schwer zu durchschauen, bis auf den von Edel. Habe den einmal in eine Prozedur gepackt und etwas verändert.

Denn er ist schnell, wenn der gesuchte String am Anfang steht. Naturgemäß langsam, wenn er erst am Ende auftaucht.

Dies kann man aber abstellen, so das der Code im Schnitt eine gute bis sehr gute Performance bringt. Auch wenn der gesuchte String sich erst im hinteren Teil befindet.

Code: Alles auswählen

quell$="Diese Prozedur sucht nicht von hinten, sondern fängt in der Mitte an. Deshalb ist diese Prozedur in vielen Fällen schneller als der Originalcode von Edel und auch schneller als die meisten hier vorgestellten Prozeduren."

such$ ="Edel"


Procedure.l FindLastString(heuhaufen.s, nadel.s, CaseSensitive=1) 

Protected pos = 0
Protected lastpos=0
Protected start=Len(heuhaufen)/2 ; <-- im 1.Repeat ab Mitte suchen

If CaseSensitive = 0 
   heuhaufen = LCase(heuhaufen) 
   nadel = LCase(nadel) 
EndIf 
  
Repeat 
  lastpos = pos 
  pos = FindString(heuhaufen,nadel,start+pos+1) 
  start=0
Until pos = 0 

If lastpos
    ProcedureReturn lastpos
Else
    pos = 0
    lastpos=0    
    Repeat 
      lastpos = pos 
      pos = FindString(heuhaufen,nadel,start+pos+1) 
    Until pos = 0 
EndIf

ProcedureReturn lastpos

EndProcedure

p=FindLastString(quell$, such$)
Debug p
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Meine von hinten suchende Prozedur etwas optimiert, weil ich sie gerade mal wieder brauchte.
Nutzt jetzt Bitshifting statt Divisionen und Multiplikationen.

Code: Alles auswählen

Procedure FindLastString(String$, StringToFind$, CaseInSensitive=0)
	Protected length=Len(StringToFind$), *position=@String$+(Len(String$)-length)<<#PB_Compiler_Unicode
	If StringToFind$
		While *position >= @String$
			If CompareMemoryString(*position, @StringToFind$, CaseInSensitive, length) = #PB_String_Equal
				ProcedureReturn (*position-@String$)>>#PB_Compiler_Unicode+1
			EndIf
			*position-SizeOf(Character)
		Wend
	EndIf
EndProcedure

Debug FindLastString("berthold", "hold")
@ hjbremer
Deine ist nicht so gut, denn du weißt nie, wo der gesuchte String tatsächlich steht. Was, wenn er genau 1 Position links vor der Hälfte steht? Das wäre das schlechteste Szenario für deine Prozedur.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Beitrag von hjbremer »

für Minimalisten :mrgreen:

Code: Alles auswählen

mem = StrRStrI_(@"Ich liebe PureBasic! Du auch?",0,@"liebe")
Parameters

pszSource
[in] Pointer to a null-terminated source string.
pszLast
[in] Pointer into the source string that defines the range of the search. Set pszLast to point to a character in the source string, and the search will stop with the preceding character. Set pszLast to NULL to search the entire source string.
pszSrch
[in] Pointer to the substring to search for.

Return Value

Returns the address of the last occurrence of the substring if successful, or NULL otherwise.
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
.:M:.
Beiträge: 44
Registriert: 29.07.2008 04:12

Beitrag von .:M:. »

Hier meine Protzidur :D

Code: Alles auswählen

Procedure FindLastString(String$, StringToFind$) 
  For i=1 To CountString(String$, StringToFind$)
    temp$=StringField(String$, i, StringToFind$)
    pos=pos+Len(temp$)+1
  Next i
  ProcedureReturn pos
EndProcedure

Debug FindLastString("anna", "a")
PB 5.11 Beta1 32Bit | Win 7 Pro 64Bit
schic
Beiträge: 68
Registriert: 25.12.2004 19:04

Beitrag von schic »

noch eine recht flotte Methode in ASM:

Code: Alles auswählen

Procedure.l inMemStrRev(StartPos, *Source, strStr.s)
  
  ; naiver Algorithmus überprüft ein gesuchtes Muster rückwärts 
  ; an allen Positionen i eines Speicherbereichs beginnend bei 
  ; StartPos, Ende der Suche bei *Source
  
  result.l
  PatLen = Len(strStr)
  
  ;init pointers etc.
  MOV Ebx,strStr    ; Ebx = Pointer to first Char in string
  MOV Edx,*Source
  MOV Ecx,Edx   ; Ecx = Pointer to akt. Char in source
  ADD Ecx,StartPos  ; set source-pointer to startposition
  SUB Ecx, PatLen
  DEC Edx
  ;INC Ecx      ; StartPos - 1
  
  ! rpt_SrcRev:        ; startpoint for loop scanning through the source
  MOV al,[Ecx]
  DEC Ecx           ; Ecx + 1
  CMP Ecx,Edx          ; if null (end of source-string)
  JS endProcRev        ; -> end Procedure, Result=0
  
  CMP byte[Ebx],al  ; if found first Char of strStr look for the rest
  JNE rpt_SrcRev       ; else go on with next Char in source
  
  ;found the first Char of strStr in source
  ;now look if the rest does match
  Xor Edi,Edi             ; Edi = 0, Edi = Pointer to akt. Char in strStr
  ! rpt_StrRev:              ; startpoint for loop pounding the string
  INC Edi                 ; Edi + 1 (the first Char in string is already found)
  
  CMP byte[Ebx+Edi],0     ; if 0 (end of strStr) 
  JE got_itRev               ; -> got it, all Chars of the string did match
  MOV al,byte[Ecx+Edi+1]  ; move actual Char in source to accumulator, have 
  ; to add 1 cause Ecx already decreased before
  
  CMP byte[Ebx+Edi],al    ; if actual Char in source (Ebx+Edi) = act Char in string (al)
  JE rpt_StrRev              ; then take next Char
  JMP rpt_SrcRev             ; and go on with scanning source
  
  ! got_itRev:
  ;Result = Ecx - Source, to get the place in the source-string
  SUB Ecx,Edx
  INC Ecx
  MOV result,Ecx
  
  ! endProcRev:
  ProcedureReturn result
EndProcedure

; und zum testen 

Macro FindReport()
  PrintN ("pattern found at position: " + Str(a) + " in " + Str(timeGetTime_()-StartTime ) + " milliseconds")
  PrintN (" ")
  a=0
EndMacro

Text.s="axbbaaxbbaaxbbabbal axbbaaxbbaaxbbabbal axbbaaxbbaaxbbabbalaxbbaaxbbaaxbbabbal This is any Text of any number of characters With no content"

findstr.s= "many"

OpenConsole()

For i = 1 To 20;8
  Text=Text+"|"+Text
Next
Text=findstr+Text

loopcount = 10;2;5000

PrintN ("starting to search " + Str(loopcount) + " times, in " + Str(Len(Text)) + " characters, for '" + findstr + "'")
PrintN (" ")


PrintN ("Search inMemStrRev")
lentxt=Len(Text)
lenfindstr=Len(findstr)
StartTime = timeGetTime_()
For i = 1 To loopcount
  a=inMemStrRev(lentxt, @Text, findstr)
Next i
FindReport()

StartPos ist die Länge des Strings wenn alles durchsucht werden soll.
Antworten