String Translate

Anfängerfragen zum Programmieren mit PureBasic.
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

String Translate

Beitrag 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
Little John

Beitrag 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
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Beitrag von Syntacks_Error »

ReplaceString(String$, SuchString$, ErsatzString$ [, Modus [, StartPosition]]) macht sowas.
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

Beitrag 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.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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...
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

Beitrag 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!
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Little John

Beitrag 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()
Zuletzt geändert von Little John am 21.06.2009 18:30, insgesamt 1-mal geändert.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Beitrag 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.
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Little John

Beitrag 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
Antworten