StringBuilder [Include]
Verfasst: 16.06.2011 00:17
Servus.
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:
Nur ist das extrem langsam, die interne Stringverwaltung von PB legt den LogString jedesmal neu an. Bei einigen 1000 LogZeilen kann das schonmal ein paar Sekunden dauern. Darum habe ich einen kleinen StringBuilder programmiert der auf den wohl wenig bekannten CopyMemoryString() Befehl basiert. Verpackt ist das Ganze in ein einfaches Interface, es sollte wirklich selbsterklärend sein.
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 :
Gruß, Alex
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))