Suche Funktion um Charakter im Memory zu zählen

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Suche Funktion um Charakter im Memory zu zählen

Beitrag von hjbremer »

Hallo, guten Abend

ich lade eine Textdatei mit ReadData ins Memory und möchte nun wissen wieviele Zeilen diese hat. Nun kann ich natürlich mit Peeks den Memoryinhalt einer Variablen zuordnen und dann mit CountString den Chr 13 zählen. Aber wat fürn Unsinn.

Ich suche also eine Funktion die die Anzahl von einem Charakter im Memory zählt.
Um einen bestimmten Chr zu finden gibt es strchr_(). Nun hab ich darum eine Schleife gebaut und zähle.

Aber gehts nicht noch einfacher ?

Code: Alles auswählen

;Demostring
a.s
a + "12345" + #CRLF$ + "6789" + #CRLF$
a + "12345" + #CRLF$ + "6789" + #CRLF$

;zur Demo String ins Memory
pointer = AllocateMemory(Len(a) + 2): PokeS(pointer, a)

;Demo für suche von chr 13 im Memory
p = strchr_(pointer, 13)
If p: Debug PeekS(p,1)
      Debug p - pointer + 1
EndIf

; --------------------------------------------------------

;Schleife für alle chr 13 zählen
p = pointer - 1
Repeat
   p = strchr_(p + 1, 13)
   If p: anz + 1: EndIf
Until Not p 

Debug anz  
PS: am besten Einfacher und Schneller

PSPS: die + 2 bei AllocateMemory ist, weil strchr_() einen nullterminierten String erwartet
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von Thorium »

Code: Alles auswählen

Procedure CountChar(*Buffer.Character, BufferLength.i, Char.c)

  Protected.i i, Count
  
  BufferCharCount = BufferLength / SizeOf(Character)
  
  For i = 1 To BufferLength
    
    If *Buffer\c = Char
      Count + 1
    EndIf
    
    *Buffer + SizeOf(Character)
  
  Next
  
  ProcedureReturn Count

EndProcedure

;Demostring
a.s
a + "12345" + #CRLF$ + "6789" + #CRLF$
a + "12345" + #CRLF$ + "6789" + #CRLF$

;zur Demo String ins Memory
pointer = AllocateMemory(Len(a)): PokeS(pointer, a)

Debug CountChar(pointer, Len(a), 13)
Bitteschön. :)
Man könnte die Längenangabe des Puffers auch weglassen, allerdings müsste man dann bei jedem Zeichen prüfen ob Null erreicht wurde, was Performance kostet und da du die Bufferlänge sowieso kennst.

Wenn dus schneller brauchst, muss man mit Assembler rann, dann ist es aber nicht mehr Plattformunabhängig.
Zuletzt geändert von Thorium am 22.03.2010 01:36, insgesamt 2-mal geändert.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von STARGÅTE »

Mir würde nur noch die Structure Variante einfallen:

Code: Alles auswählen

;Demostring
a.s
a + "12345" + #CRLF$ + "6789" + #CRLF$
a + "12345" + #CRLF$ + "6789" + #CRLF$

;zur Demo String ins Memory
*pointer.Character = AllocateMemory(Len(a) + SizeOf(Character)) : PokeS(*pointer, a)

;Demo für suche von chr 13 im Memory
While *pointer\c
 If *pointer\c = 13 : anz+1 : EndIf
 *pointer+SizeOf(Character)
Wend
Debug anz  
EDIT: Zu spääät ^^
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von hjbremer »

Habe einen kleinen Vergleich gemacht

Code: Alles auswählen

Structure myFileInfo
 name.s
 laenge.i
 StructureUnion
  inhalt.s
  zeiger.i
 EndStructureUnion
EndStructure

Procedure.i DatenLesen(*dat.myFileInfo)

With *dat
   Protected dnr = ReadFile(#PB_Any, \name)
   If dnr
      \laenge = Lof(dnr)
      \inhalt = Space(\laenge)
      \laenge = ReadData(dnr, \zeiger, \laenge)
      CloseFile(dnr)  
   Else
      MessageRequester(\name,"Konnte diese Datei nicht öffnen!")
      End
   EndIf
EndWith

EndProcedure

Procedure CountChar1(*Buffer.Character, BufferLength)
; von Thorium

  Protected.i i, Count
    
  For i = 1 To BufferLength    
    If *Buffer\c = 13
      Count + 1
    EndIf    
    *Buffer + SizeOf(Character)  
  Next
  
  ProcedureReturn Count
EndProcedure

Procedure CountChar2(*pointer.Character)
; von Stargate

Protected anz

While *pointer\c
   If *pointer\c = 13 : anz + 1 : EndIf
   *pointer + SizeOf(Character)
Wend

ProcedureReturn anz 
EndProcedure

Procedure CountChar3(pointer)
; von Bremer

Protected p = pointer - SizeOf(Character)
Protected anz

While p
   p = strchr_(p + SizeOf(Character), 13)
   If p: anz + 1: EndIf
Wend 

ProcedureReturn anz 
EndProcedure


datei.myFileInfo
datei\name = #PB_Compiler_Home + "Compilers\APIFunctionListing.txt"

DatenLesen(datei)

max = 250

a = GetTickCount_()
  For j = 1 To max: anz = CountChar1(datei\zeiger, datei\laenge): Next
thorium = GetTickCount_() - a
i$ = Str(anz) + " thorium  " + Str(thorium) + #LF$

a = GetTickCount_()
  For j = 1 To max: anz = CountChar2(datei\zeiger): Next
stargate = GetTickCount_() - a
i$ + Str(anz) + " stargate " + Str(stargate) + #LF$

a = GetTickCount_()
  For j = 1 To max: anz = CountChar3(datei\zeiger): Next
strchr = GetTickCount_() - a
i$ + Str(anz) + " strchr   " + Str(strchr) + #LF$

MessageRequester("", i$)
Geht da noch was ? Eventuell mit ASM

Ich persönlich benötige keine Plattformunabhängigkeit
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von Thorium »

Mit Assembler geht da noch ne Menge.
Reichts dir wenn die Prozedur nur ASCII/ANSI verarbeitet oder brauchst du auch Unicode?
Nur ASCII/ANSI ist um einiges einfacher in Assembler. Bei Unicode müsste man bissel rumwurschteln um keine 16bit Operationen zu haben, die saulahm sind.

Und sind deine Strings immer so klein? Wenn sie groß sind kann man auch noch MMX/SSE2 verwenden. Aber bei so kleinen Strings würde das in einem Performanceverlust resultieren.

Also schreib nochmal was genau du brauchst und ich bastel die ne Assemblerprozedur heute abend.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von hjbremer »

Unicode ? neue Pilzsorte, Fernsehsendung, oder ... ?

nein brauch ich nicht, progge nur für mich und Umgebung

Textdateien können ohne Probleme 5 Mb erreichen, darum ja auch in Speicher laden und auswerten.

Danke im voraus
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von Thorium »

Bitteschön, ist gut doppelt so schnell wie die anderen. Das ist jetzt simples Assembler, ich werd noch eine mit SSE2 schreiben, das wird aber heute nix mehr.

Code: Alles auswählen

Structure myFileInfo
name.s
laenge.i
StructureUnion
  inhalt.s
  zeiger.i
EndStructureUnion
EndStructure

Procedure.i DatenLesen(*dat.myFileInfo)

With *dat
   Protected dnr = ReadFile(#PB_Any, \name)
   If dnr
      \laenge = Lof(dnr)
      \inhalt = Space(\laenge)
      \laenge = ReadData(dnr, \zeiger, \laenge)
      CloseFile(dnr)
   Else
      MessageRequester(\name,"Konnte diese Datei nicht öffnen!")
      End
   EndIf
EndWith

EndProcedure

Procedure CountChar1(*Buffer.Character, BufferLength)
; von Thorium

  Protected.i i, Count
   
  For i = 1 To BufferLength   
    If *Buffer\c = 13
      Count + 1
    EndIf   
    *Buffer + SizeOf(Character)
  Next

  ProcedureReturn Count
EndProcedure

Procedure CountChar2(*pointer.Character)
; von Stargate

Protected anz

While *pointer\c
   If *pointer\c = 13 : anz + 1 : EndIf
   *pointer + SizeOf(Character)
Wend

ProcedureReturn anz
EndProcedure

Procedure CountChar3(pointer)
; von Bremer

Protected p = pointer - SizeOf(Character)
Protected anz

While p
   p = strchr_(p + SizeOf(Character), 13)
   If p: anz + 1: EndIf
Wend

ProcedureReturn anz
EndProcedure

Procedure.i CountCharAsm(*Buffer, BufferLength.i, Char.a)

  CompilerSelect #PB_Compiler_Processor

    CompilerCase #PB_Processor_x86
    
      !push edi
      !cld
      !xor edx,edx
      !mov ecx,[p.v_BufferLength+4]
      !mov edi,[p.p_Buffer+4]
      !mov al,[p.v_Char+4]
     
      !align 4
      !CountCharAsmLoop:
        !repne scasb
        !inc edx
        !test ecx,ecx
      !jne CountCharAsmLoop
     
      !mov bl,[edi]
      !cmp bl,al
      !je CountCharAsmEnd
        !dec edx 
      !CountCharAsmEnd:
     
      !mov eax, edx
      !pop edi

    CompilerCase #PB_Processor_x64
   
      !push rdi
      !cld
      !xor rdx,rdx
      !mov rcx,[p.v_BufferLength+8]
      !mov rdi,[p.p_Buffer+8]
      !mov al,[p.v_Char+8]
     
      !align 8
      !CountCharAsmLoop:
        !repne scasb
        !inc rdx
        !test rcx,rcx
      !jne CountCharAsmLoop
     
      !mov bl,[rdi]
      !cmp bl,al
      !je CountCharAsmEnd
        !dec rdx 
      !CountCharAsmEnd:
     
      !mov rax, rdx
      !pop rdi
      
  CompilerEndSelect

  ProcedureReturn

EndProcedure

datei.myFileInfo
datei\name = #PB_Compiler_Home + "Compilers\APIFunctionListing.txt"

DatenLesen(datei)

max = 500

a = GetTickCount_()
  For j = 1 To max: anz = CountChar1(datei\zeiger, datei\laenge): Next
thorium = GetTickCount_() - a
i$ = Str(anz) + " thorium  " + Str(thorium) + #LF$

a = GetTickCount_()
  For j = 1 To max: anz = CountChar2(datei\zeiger): Next
stargate = GetTickCount_() - a
i$ + Str(anz) + " stargate " + Str(stargate) + #LF$

a = GetTickCount_()
  For j = 1 To max: anz = CountChar3(datei\zeiger): Next
strchr = GetTickCount_() - a
i$ + Str(anz) + " strchr   " + Str(strchr) + #LF$

a = GetTickCount_()
  For j = 1 To max: anz = CountCharAsm(datei\zeiger, datei\laenge, 13): Next
SimpleAsm = GetTickCount_() - a
i$ + Str(anz) + " simples Assembler   " + Str(SimpleAsm) + #LF$


MessageRequester("", i$)
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von hjbremer »

Boooaah, Wahnsinn, affengeil :praise:

Nun noch eine kleine Frage, könnte man sich nicht den Längenparameter sparen und auf Nullstring prüfen?

So in der Art:
CMP byte[ecx], 0 ;überprüfen ob Stringende erreicht
JZ l_ende ;wenn ja, jump nach ende

Da ich aber von ASM so gut wie null Ahnung habe und nie weiß welche Register etc, bekomme ich es nie auf die Reihe.

Oder wird das ganze dann zu langsam ?
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von Thorium »

Kann man schon, allerdings kann man dann nicht mehr repne scasb verwenden, das scannt den String solange bis das AL Register gleich dem Byte addressiert durch das EDI Register ist, wobei es bei jedem durchlauf automatisch höher zählt. Außerdem wird repne auch beendet wenn ECX gleich null ist. ECX wird mit jedem durchlauf eins runtergezählt und enthält die Länge des strings. REPNE SCASB ist quasie eine komplette Schleife in sich. Und diese Instruktion gehöhrt zu den wenigen Repetier-Stringbefehlen, welche hochoptimiert sind. Im Klartext baut man die Schleife so auf das die Länge innerhalb der Schleife geprüft wird, wird es merkbar langsamer werden.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Suche Funktion um Charakter im Memory zu zählen

Beitrag von hjbremer »

Aha, also ist es besser so wie es ist, Danke für die Info !
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
Antworten