String filtering/custom ReplaceString+inplace

Share your advanced PureBasic knowledge/code with the community.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

String filtering/custom ReplaceString+inplace

Post by Lunasole »

Here is some small procedure I've made daily to filter strings, close to ReplaceString with #PB_String_InPlace flag, but also allowing customization.
Additionaly, it is optimized and fast enough.

// Updated to a much faster version, thanks to Josh

Code: Select all

EnableExplicit

; This proc filters string, leaving only characters allowed in "Select" section
; *S			pointer to a string or memory (must be 0-terminated)
; RETURN:		none, filtered string returned byref
Procedure StringFilter (*S.Character)
   Protected *C.Character = *S
   Repeat
      Select *S\c
         Case 'a' To 'z', 'A' To 'Z', ' ', '0' To '9':
         	*C\c = *S\c
         	*C + SizeOf(Character)
         Case 0:
         	*C\c = 0
         	Break
      EndSelect 
      *S + SizeOf(Character)
   ForEver
EndProcedure


; example
Define A$ = "so*me stri@ng to$ filte%r"
StringFilter(@A$)
Debug A$
Last edited by Lunasole on Thu Jan 05, 2017 11:58 am, edited 1 time in total.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
WilliamL
Addict
Addict
Posts: 1252
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: String filtering/custom ReplaceString+inplace

Post by WilliamL »

Very interesting!

Thanks :D
MacBook Pro-M1 (2021), Sequoia 15.4, PB 6.20
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: String filtering/custom ReplaceString+inplace

Post by Josh »

Hello Luna,

I have been working on this topic lately. Here is my quick and dirty version:

Code: Select all

EnableExplicit


Procedure StringFilter (*Read.Character)
  Define *Write.Character = *Read

  Repeat

    If     *Read\c  = #Null                 : *Write\c = *Read\c : ProcedureReturn
    ElseIf *Read\c  = ' '                   : *Write\c = *Read\c : *Write + SizeOf (Character)
    ElseIf *Read\c >= 'a' And *Read\c <= 'z': *Write\c = *Read\c : *Write + SizeOf (Character)
    ElseIf *Read\c >= 'A' And *Read\c <= 'Z': *Write\c = *Read\c : *Write + SizeOf (Character)
    ElseIf *Read\c >= '0' And *Read\c <= '0': *Write\c = *Read\c : *Write + SizeOf (Character)
    EndIf

    *Read + SizeOf (Character)
  ForEver

EndProcedure



; example
Define A$ {100}
Define timer
Define i

timer = ElapsedMilliseconds()
For i = 1 To 1000000

  A$ = "so*me stri@ng to$ filte%r"
  StringFilter(@A$)

Next
timer = ElapsedMilliseconds() - timer
MessageRequester ("", A$ + #CRLF$ + timer)
sorry for my bad english
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: String filtering/custom ReplaceString+inplace

Post by Lunasole »

Josh wrote:Hello Luna,
I have been working on this topic lately. Here is my quick and dirty version
Thanks, nicely done ^^. That your version is simpler and works ~25% faster.
I've improved my own using the same stuff without PeekC, extra variables, etc.

And got unexpected results in speed -- that version with Select Case became 30% faster then.
Well, maybe because I've moved Case 0 to the end

Code: Select all

EnableExplicit
DisableDebugger
; 1. Initial params and common code
OpenConsole("[Testing Facility v1.0.0.1] :3")
Define Time1, Time2, Counter, Count = 1000000 ; 1kk
Macro TestCode (Time, Code)
	Time = ElapsedMilliseconds()
	For Counter = 1 To Count
		Code
	Next Counter
	Time = ElapsedMilliseconds() - Time
EndMacro

; 2. User code
; --------------------------------------------------- \
; declare someting
Procedure StringFilter (*S.Character)
   Protected *C.Character = *S
   Repeat
      Select *S\c
         Case 'a' To 'z', 'A' To 'Z', ' ', '0' To '9':
         	*C\c = *S\c
         	*C + SizeOf(Character)
         Case 0:
         	*C\c = 0
         	Break
      EndSelect 
      *S + SizeOf(Character)
   ForEver
EndProcedure
Procedure StringFilter2 (*Read.Character)
  Define *Write.Character = *Read

  Repeat

    If     *Read\c  = #Null                 : *Write\c = *Read\c : ProcedureReturn
    ElseIf *Read\c  = ' '                   : *Write\c = *Read\c : *Write + SizeOf (Character)
    ElseIf *Read\c >= 'a' And *Read\c <= 'z': *Write\c = *Read\c : *Write + SizeOf (Character)
    ElseIf *Read\c >= 'A' And *Read\c <= 'Z': *Write\c = *Read\c : *Write + SizeOf (Character)
    ElseIf *Read\c >= '0' And *Read\c <= '0': *Write\c = *Read\c : *Write + SizeOf (Character)
    EndIf

    *Read + SizeOf (Character)
  ForEver

EndProcedure

Define A$
Macro Code1 ; code executed inside cycle 1
	A$ = "so*me stri@ng to$ filte%r"
	StringFilter(@A$)
	; do something-1
EndMacro

Macro Code2 ; code executed inside cycle 2
	A$ = "so*me stri@ng to$ filte%r"
	StringFilter2(@A$)
	; do something-2
EndMacro
; --------------------------------------------------- /

; 3. Execute tests
TestCode(Time1, Code1)
TestCode(Time2, Code2)
; 4. Display results
PrintN("Cycle 1: " + Str(Time1) + "ms")
PrintN("Cycle 2: " + Str(Time2) + "ms")
PrintN("------------")
If Time2 > Time1
	PrintN("First is faster: " + StrF(100.0 - (Time1 / (Time2 / 100.0)), 2) + "%")
ElseIf Time1 > Time2
	PrintN("Second is faster: " +StrF(100.0 - (Time2 / (Time1 / 100.0)), 2) + "%")
Else
	PrintN("Equal results")
EndIf
Input()
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Post Reply