Seite 1 von 3
String Translate
Verfasst: 19.06.2009 09:55
von PeterJ
Guten Morgen!
Gibt es sowas wie einen String Translate bei dem ich in einem Rutsch bestimmte Zeichen in etwas anderes umwandeln kann?
Ich komme vom IBM Assembler und dort ist das sogar Bestandteil des CPU Instruction Sets. Auch REXX kann sowas, dort würde es man z.B. so schreiben:
buffer=translate(buffer,"AXZ","123")
d.h. jede 1 im String würde in A, jede 2 in X, und die 3 in Z umgewandelt werden.
Ich möchte es gerne verwenden um einen TCP Buffer von allen nicht druckbaren Zeichen zu bereinigen, also diese in Blank umwanden.
Mir ist schon klar dass ich das in einer Schleife programmieren kann, aber sonderlich schnell ist das nicht.
Peter
Verfasst: 19.06.2009 10:09
von Little John
Hallo,
das schnellste in PureBasic ist wahrscheinlich sowas
Code: Alles auswählen
s$ = "AXZ"
ReplaceString(s$, "A", "1", #PB_String_InPlace )
ReplaceString(s$, "X", "2", #PB_String_InPlace )
ReplaceString(s$, "Z", "3", #PB_String_InPlace )
Debug s$
Falls die Geschwindigkeit nicht reichen sollte, müsstest Du wohl die selbstprogrammierte Schleife in Assembler schreiben ( also quasi die gewünschte Translate()-Funktion selbst implementieren

).
Gruß, Little John
Verfasst: 19.06.2009 10:18
von Syntacks_Error
ReplaceString(String$, SuchString$, ErsatzString$ [, Modus [, StartPosition]]) macht sowas.
Verfasst: 19.06.2009 10:41
von PeterJ
ReplaceString ist definitiv zu langsam, speziell wenn Du lange Buffer und viele Translate Characters hast. Wenn schon in PB dann wie folgt:
Hier wird 1 durch A , 2 durch B und 3 durch C ersetzt.
Code: Alles auswählen
MyBuffer.s="Record 1; Record 2; Record 3"
;
; Set full ASCII character set
Global Dim trfull.b(255)
For i = 0 To 255
TRFULL(i)=i
Next
; Now change the characters to replace
trfull(Asc("1"))=Asc("A")
trfull(Asc("2"))=Asc("B")
trfull(Asc("3"))=Asc("C")
; run through buffer and replace it's character as defined in translation table
*ptr.BYTE
*ptr=@MyBuffer
For i = 0 To Len(MyBuffer)
*ptr\b=trfull(*ptr\b)
*ptr+1
Next
Debug MyBuffer
... aber wie gesagt sicher auch nicht der schnellste Ansatz.
Verfasst: 19.06.2009 10:49
von Kaeru Gaman
hm...
vermeide mal die Calls innerhalb der Schleife..
sind die zu ersetzenden Zeichen Hardcoded, also immer die selben?
dann geh den String doch mit ner schleife durch und prüfe mit Case?
braucht mehr platz aber ist schneller als ne prüfschleife in der durchgeschleife...
Verfasst: 19.06.2009 10:53
von PeterJ
Kaeru Gaman hat geschrieben:hm...
vermeide mal die Calls innerhalb der Schleife..
Welche calls? ist doch nur ein Array.
Kaeru Gaman hat geschrieben:sind die zu ersetzenden Zeichen Hardcoded, also immer die selben?
Nach dem Gesetz der größten Gemeinheit: nein, natürlich nicht immer die selben!
Verfasst: 19.06.2009 12:13
von Kaeru Gaman
> ist doch nur ein Array
oops, sorry. hab ich nich richtig hingeschaut.
öhjoar...
> aber wie gesagt sicher auch nicht der schnellste Ansatz.
doch.
einfache schleife mit lookup-table ist bestimmt das schnellste.
Verfasst: 21.06.2009 18:07
von Little John
Ich habe hier jetzt nochmal 3 Vorgehensweisen miteinander verglichen (die beiden vorgestellten Prozeduren funktionieren allerdings nicht mit Unicode). Ich hoffe, die ASM-Prozedur enthält keine Fehler. Vielleicht mag -- falls nötig -- ein
heller Kopf sie korrigieren und/oder noch beschleunigen.
Gruß, Little John
//edit: Debug-Anweisungen ersetzt
Code: Alles auswählen
; getestet mit PB 4.31 unter Windows XP
EnableExplicit
Procedure Translate1 (*text, textLen, Array trans.b(1))
Protected *textPtr.Byte
; iterate through memory and replace bytes
For *textPtr = *text To *text + textLen - 1
*textPtr\b = trans(*textPtr\b)
Next
EndProcedure
Procedure Translate2 (*text, textLen, *trans)
; iterate through memory and replace bytes
! mov edx, [p.p_text]
! mov ecx, [p.v_textLen]
! mov eax, [p.p_trans]
! push ebx
! mov ebx, eax
! TransLoop:
! mov al, [edx+ecx]
! xlatb
! mov [edx+ecx], al
! loop TransLoop
! pop ebx
EndProcedure
;-- Demo
Define n, i, t
Define old$, new$, s$
n = 30000 ; Anzahl der Schleifen (evtl. je nach PC verkleinern oder vergrößern)
old$ = "123456789"
new$ = "ABCDEFGHI"
; build translation table
Dim trans.b(255)
For i = 0 To 255
trans(i) = i
Next
For i = 1 To Len(old$)
trans(Asc(Mid(old$,i,1))) = Asc(Mid(new$,i,1))
Next
OpenConsole()
; translate string (0)
s$ = "Record 1; Record 2; Record 3; Record 4; Record 5; Record 6; Record 7; Record 8; Record 9"
t = ElapsedMilliseconds()
For i = 1 To n
ReplaceString(s$, "1", "A", #PB_String_InPlace)
ReplaceString(s$, "2", "B", #PB_String_InPlace)
ReplaceString(s$, "3", "C", #PB_String_InPlace)
ReplaceString(s$, "4", "D", #PB_String_InPlace)
ReplaceString(s$, "5", "E", #PB_String_InPlace)
ReplaceString(s$, "6", "F", #PB_String_InPlace)
ReplaceString(s$, "7", "G", #PB_String_InPlace)
ReplaceString(s$, "8", "H", #PB_String_InPlace)
ReplaceString(s$, "9", "I", #PB_String_InPlace)
Next
t = ElapsedMilliseconds() - t
PrintN("'" + s$ + "'" + #LF$ + Str(t) + " ms")
; translate string (1)
s$ = "Record 1; Record 2; Record 3; Record 4; Record 5; Record 6; Record 7; Record 8; Record 9"
t = ElapsedMilliseconds()
For i = 1 To n
Translate1(@s$, Len(s$), trans())
Next
t = ElapsedMilliseconds() - t
PrintN("'" + s$ + "'" + #LF$ + Str(t) + " ms")
; translate string (2)
s$ = "Record 1; Record 2; Record 3; Record 4; Record 5; Record 6; Record 7; Record 8; Record 9"
t = ElapsedMilliseconds()
For i = 1 To n
Translate2(@s$, Len(s$), @trans(0))
Next
t = ElapsedMilliseconds() - t
PrintN("'" + s$ + "'" + #LF$ + Str(t) + " ms")
Input()
Verfasst: 21.06.2009 18:11
von cxAlex
Das ganze mit Debugger zu testen bringt gar nix:
Code: Alles auswählen
; getestet mit PB 4.31 unter Windows XP
EnableExplicit
OpenConsole()
Procedure Translate1 (*text, textLen, Array trans.b(1))
Protected *textPtr.Byte
; iterate through memory and replace bytes
For *textPtr = *text To *text + textLen - 1
*textPtr\b = trans(*textPtr\b)
Next
EndProcedure
Procedure Translate2 (*text, textLen, *trans)
; iterate through memory and replace bytes
! mov edx, [p.p_text]
! mov ecx, [p.v_textLen]
! mov eax, [p.p_trans]
! push ebx
! mov ebx, eax
! TransLoop:
! mov al, [edx+ecx]
! xlatb
! mov [edx+ecx], al
! loop TransLoop
! pop ebx
EndProcedure
;-- Demo
Define n, i, t
Define old$, new$, s$
n = 20000 ; Anzahl der Schleifen (evtl. je nach PC verkleinern oder vergrößern)
old$ = "123456789"
new$ = "ABCDEFGHI"
; build translation table
Dim trans.b(255)
For i = 0 To 255
trans(i) = i
Next
For i = 1 To Len(old$)
trans(Asc(Mid(old$,i,1))) = Asc(Mid(new$,i,1))
Next
; translate string (0)
s$ = "Record 1; Record 2; Record 3; Record 4; Record 5; Record 6; Record 7; Record 8; Record 9"
t = ElapsedMilliseconds()
For i = 1 To n
ReplaceString(s$, "1", "A", #PB_String_InPlace)
ReplaceString(s$, "2", "B", #PB_String_InPlace)
ReplaceString(s$, "3", "C", #PB_String_InPlace)
ReplaceString(s$, "4", "D", #PB_String_InPlace)
ReplaceString(s$, "5", "E", #PB_String_InPlace)
ReplaceString(s$, "6", "F", #PB_String_InPlace)
ReplaceString(s$, "7", "G", #PB_String_InPlace)
ReplaceString(s$, "8", "H", #PB_String_InPlace)
ReplaceString(s$, "9", "I", #PB_String_InPlace)
Next
t = ElapsedMilliseconds() - t
PrintN("'" + s$ + "'")
PrintN(Str(t) + " ms")
; translate string (1)
s$ = "Record 1; Record 2; Record 3; Record 4; Record 5; Record 6; Record 7; Record 8; Record 9"
t = ElapsedMilliseconds()
For i = 1 To n
Translate1(@s$, Len(s$), trans())
Next
t = ElapsedMilliseconds() - t
PrintN("'" + s$ + "'")
PrintN(Str(t) + " ms")
; translate string (2)
s$ = "Record 1; Record 2; Record 3; Record 4; Record 5; Record 6; Record 7; Record 8; Record 9"
t = ElapsedMilliseconds()
For i = 1 To n
Translate2(@s$, Len(s$), @trans(0))
Next
t = ElapsedMilliseconds() - t
PrintN("'" + s$ + "'")
PrintN(Str(t) + " ms")
Input()
die 2. und 3. Procedure sind bei mir ziemlich gleichschnell (einmal hat die 2. 16 ms und die 3. 0, dann beide 0, ...) dürfte die Ungenauigkeit von ElapsedMilliseconds() sein.
Verfasst: 21.06.2009 18:39
von Little John
cxAlex hat geschrieben:Das ganze mit Debugger zu testen bringt gar nix:
Stimmt natürlich. Leider passiert es mir schonmal, dass ich nicht daran denke. Ich habe den Code in meinem vorigen Beitrag jetzt entspr. korrigiert.
cxAlex hat geschrieben:die 2. und 3. Procedure sind bei mir ziemlich gleichschnell (einmal hat die 2. 16 ms und die 3. 0, dann beide 0, ...) dürfte die Ungenauigkeit von ElapsedMilliseconds() sein.
Ich habe jetzt noch mal die Anzahl der Schleifen vergrößert, damit ich keine Ergebnisse von 0 ms bekomme.
Translate2() benötigte dabei bei mir immer 15 ms, Translate1() bei den beiden ersten Durchläufen je 30 ms, beim 3. Durchlauf 16 ms, und beim 4. Durchlauf wieder 30 ms. Ich schätze das hat was mit dem Cache zu tun. Vielleicht lässt sich die ASM-Prozedur ja auch noch beschleunigen, ich bin dafür kein Experte.
Gruß, Little John