Es kommt häufig zu der Situation das man einem String immer wieder vergrößert, meist in einer Schleife, zum Erzeugen von Datei-Inhalten, Logs, sonstiges (zeilenweises) Erstellen einer Text - Datei, .... .
Also ca. diese Form von Code:
Code: Alles auswählen
Define Log$ = ""
For i = 1 To LogEinträge
; Einträge laden...
Log$ + LogZeile$
Next
; .....
Ein kleiner Demo-Code ist am Ende des Codes der die "normale" PB - Variante und den String-Builder gegenüberstellen. Bei mir benötigt PB ~35.000 ms, Der StringBuilder 0 ms!
StringBuilder.pbi :
Code: Alles auswählen
; ------------------------------------------------------------------------------------
; StringBuilder
; PB 4.5+ OS: Windows/Linux/MacOS Architecture: x86/x64
; Author: Alexander Aigner, cxAlex @ purebasic.fr/german
; ------------------------------------------------------------------------------------
; EnableExplicit
Structure _StringBuilder_Data
*VTable
*StringBuffer
*StringBufferPointer
*StringBufferPointerPointer
StringSize.i
BlockSize.i
BlockCount.i
EndStructure
Interface StringBuilder ; StringBuilder Interface
Add(*String) ; String hinzufügen (Pointer auf String)
GetString.s() ; Totalen String laden
Free() ; StringBuilder freigeben
EndInterface
; Neuer StringBuilder.
; BlockSize: mit welcher Blockgröße soll der Speicher erweitert werden?
Procedure StringBuilder_New(BlockSize = 1024*1024)
Protected *SB._StringBuilder_Data = AllocateMemory(SizeOf(_StringBuilder_Data))
With *SB
\VTable = ?_StringBuilder_VTable
\StringSize = #Null
; Speicher anlegen
\BlockSize = BlockSize
\BlockCount = #Null
\StringBuffer = #Null
\StringBufferPointer = #Null
\StringBufferPointerPointer = @\StringBufferPointer ; Zeiger auf StringBufferPointer
EndWith
ProcedureReturn *SB
EndProcedure
Procedure _StringBuilder_Add(*SB._StringBuilder_Data, *String)
Protected StringSize = MemoryStringLength(*String)
With *SB
; Muss resizen
While (\BlockCount*\BlockSize) < (\StringSize+StringSize+SizeOf(Character))
\BlockCount + 1
\StringBuffer = ReAllocateMemory(\StringBuffer, \BlockCount*\BlockSize)
\StringBufferPointer = \StringBuffer + \StringSize
Wend
; Kopieren
; CopyMemoryString Erhöht den \StringBufferPointer auf den \StringBufferPointerPointer zeigt selbst um StringSize,
; Deshabl müssen wir das nicht selbst machen.
CopyMemoryString(*String, \StringBufferPointerPointer)
; Stringgröße erhöhen
\StringSize+StringSize
EndWith
EndProcedure
Procedure.s _StringBuilder_GetString(*SB._StringBuilder_Data)
With *SB
If \StringBuffer
; Zurückgeben
ProcedureReturn PeekS(\StringBuffer, \StringSize)
EndIf
EndWith
EndProcedure
Procedure _StringBuilder_Free(*SB._StringBuilder_Data)
With *SB
; Freigeben
If \StringBuffer
FreeMemory(\StringBuffer)
EndIf
FreeMemory(*SB)
EndWith
EndProcedure
DataSection
_StringBuilder_VTable:
Data.i @_StringBuilder_Add()
Data.i @_StringBuilder_GetString()
Data.i @_StringBuilder_Free()
EndDataSection
; TEST
#NB = 20000
Define ResultString$, ResultString2$
Define String$ = "####################################### Hallo!! #######################################" + #LFCR$
Define i, t1, t2
; Normal zusammenfügen:
t1 = ElapsedMilliseconds()
For i = 1 To #NB
ResultString$ + String$
Next
t1 = ElapsedMilliseconds() - t1
; StringBuilder:
t2 = ElapsedMilliseconds()
Define mSB.StringBuilder = StringBuilder_New()
For i = 1 To #NB
mSB\Add(@String$)
Next
ResultString2$ = mSB\GetString()
mSB\Free()
t2 = ElapsedMilliseconds() - t2
; Checksummen berechnen zum Vergleich:
Define CRC1 = CRC32Fingerprint(@ResultString$, StringByteLength(ResultString$))
Define CRC2 = CRC32Fingerprint(@ResultString2$, StringByteLength(ResultString2$))
MessageRequester("","PB Stringzusammenfügen:" + Chr(9) +Str(t1) + " ms, CRC:" + Chr(9)+ Str(CRC1) + Chr(13) + "Stringbuilder:" + Chr(9) + Chr(9) + Str(t2) + " ms, CRC:" + Chr(9) + Str(CRC2))