Write-, ReadStructure (3. Update)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Write-, ReadStructure (3. Update)

Beitrag von NicTheQuick »

Hallo Leute,

ich habe hier mal wieder meine altbewährten Funktionen zum Schreiben und
Lesen von strukturierten Variablen in und aus einer Datei und zum Kopieren
einer strukturierten Variablen in eine andere ausgepackt und erneuert.

///Edit 1:
+ Funktionen etwas verkürzt.

///Edit 2:
+ Vielleicht etwas schneller
+ WriteSizeStructure() zum Ermitteln der Bytes, die bei WriteStructure() geschrieben werden

///Edit 3:
+ Vereinfachung für Strukturen mit statischen Arrays (s. Beispiel)

///Edit 4:
+ Ascii, Unicode und Integer hinzugefügt
+ CopyStructure() entfernt, da es das jetzt nativ gibt
+ Funktioniert erst ab der nächsten Beta

///Edit 5:
+ Jetzt geht es auch mit der aktuellen Beta

///Edit 6:
+ Proceduren geben nun einen Integer zurück (Danke ts-soft)
+ Beispiele sind kompatibel zu Windows und Linux (Danke ts-soft)

///Edit 7:
+ Code verkürzt
+ Code schneller gemacht

Der folgende Code funktioniert mit PB V4.50 beta 3 unter Linux und Windows.

Code: Alles auswählen

Structure test1
	id.l
	Name.s
	points.f[10]  ;<-----------
EndStructure         ;        |
Structure Strukt     ;        |
	Byte.b           ;        |
	word.w           ;        |
	Long.l           ;        |
	Quad.q           ;        |
	String1.s        ;        |
	Float.f          ;        |
	test1.test1[5]   ;<----   |
	String2.s        ;    |   |
	Double.d         ;    |   |
EndStructure         ;    |   |
Global Struc_Strukt.s    ;|   |
Struc_Strukt =    "bwlqsf[5ls[10f]]sd" ; Die Struktur selbst in einem String

Structure AllTypes
	StructureUnion
		b.b : c.c : w.w : l.l : f.f : d.d : q.q : a.a : u.u : i.i
	EndStructureUnion
EndStructure
Procedure.i WriteSizeStructure(*Var.AllTypes, *Struc.Character, *extralength.Integer = 0)
	Protected length.l = 0, mode.l = 0, c.l, *Start, alllength.l = 0, strlength.i
	
	While *Struc\c And (*Struc\c <> ']' Or mode)
		If mode = 0
			If *Struc\c = 's'
				*Var + length
				alllength + length + SizeOf(String)
				If *extralength
					*extralength\i + Len(PeekS(*Var\i))
				Else
					strlength + Len(PeekS(*Var\i))
				EndIf
				*Var + SizeOf(String)
				length = 0
			ElseIf *Struc\c = '['
				*Var + length
				alllength + length
				*Start = *Struc
				mode = 1
				c = 0
				length = 0
			Else
				length + PeekA(?WriteReadStructure + *Struc\c - 'a')
			EndIf
		Else
			Select *Struc\c
				Case '[' : mode + 1 ;weitere Verschachtelungen ignorieren
				Case ']' : mode - 1
				Default
					If *Struc\c >= '0' And *Struc\c <= '9' And mode = 1
						c * 10 + *Struc\c - '0'
						*Start + SizeOf(Character)
					EndIf
			EndSelect
			If mode = 0 ;Wenn Ende der Verschachtelung gefunden
				While c
					length = WriteSizeStructure(*Var, *Start + SizeOf(Character), @strlength)
					alllength + length
					*Var + length
					c - 1
				Wend
				length = 0
			EndIf
		EndIf
		*Struc + SizeOf(Character)
	Wend
	ProcedureReturn alllength + length + strlength
EndProcedure
Procedure.i WriteStructure(FileID, *Var.AllTypes, *Struc.Character)
	Protected length.l = 0, mode.l = 0, c.l, *Start, alllength.l = 0
	
	While *Struc\c And (*Struc\c <> ']' Or mode)
		If mode = 0
			If *Struc\c = 's'
				If length : WriteData(FileID, *Var, length) : *Var + length : alllength + length : EndIf
				length = StringByteLength(PeekS(*Var\i))
				WriteInteger(FileID, length)
				If length : WriteData(FileID, *Var\i, length) : EndIf
				alllength + SizeOf(Integer)
				*Var + SizeOf(Integer)
				length = 0
			ElseIf *Struc\c = '['
				If length : WriteData(FileID, *Var, length) : *Var + length : alllength + length : EndIf
				*Start = *Struc
				mode = 1
				c = 0
				length = 0
			Else
				length + PeekA(?WriteReadStructure + *Struc\c - 'a')
			EndIf
		Else
			Select *Struc\c
				Case '[' : mode + 1 ;weitere Verschachtelungen ignorieren
				Case ']' : mode - 1
				Default
					If *Struc\c >= '0' And *Struc\c <= '9' And mode = 1
						c * 10 + *Struc\c - '0'
						*Start + SizeOf(Character)
					EndIf
			EndSelect
			If mode = 0 ;Wenn Ende der Verschachtelung gefunden
				While c
					length = WriteStructure(FileID, *Var, *Start + SizeOf(Character))
					alllength + length
					*Var + length
					c - 1
				Wend
				length = 0
			EndIf
		EndIf
		*Struc + SizeOf(Character)
	Wend
	If length : WriteData(FileID, *Var, length) : EndIf
	ProcedureReturn alllength + length
EndProcedure
Procedure.i ReadStructure(FileID, *Var.AllTypes, *Struc.Character)
	Protected length.l, alllength.l, mode.l, c.l, *Start, *s.String, *mem
	
	While *Struc\c And (*Struc\c <> ']' Or mode)
		If mode = 0
			If *Struc\c = 's'
				If length : ReadData(FileID, *Var, length) : *Var + length : alllength + length : EndIf
				length = ReadInteger(FileID)
				*s = *Var
				InitializeStructure(*s, String)
				If length
					*mem = AllocateMemory(length)
					If *mem
						ReadData(FileID, *mem, length)
						*s\s = PeekS(*mem, length)
						FreeMemory(*mem)
					EndIf
				EndIf
				alllength + SizeOf(Integer)
				*Var + SizeOf(Integer)
				length = 0
			ElseIf *Struc\c = '['
				If length : ReadData(FileID, *Var, length) : *Var + length : alllength + length : EndIf
				*Start = *Struc
				mode = 1
				c = 0
				length = 0
			Else
				length + PeekA(?WriteReadStructure + *Struc\c - 'a')
			EndIf
		Else
			Select *Struc\c
				Case '[' : mode + 1
				Case ']' : mode - 1
				Default
					If *Struc\c >= '0' And *Struc\c <= '9' And mode = 1
						c * 10 + *Struc\c - '0'
						*Start + SizeOf(Character)
					EndIf
			EndSelect
			If mode = 0
				While c
					length = ReadStructure(FileID, *Var, *Start + SizeOf(Character))
					alllength + length
					*Var + length
					c - 1
				Wend
				length = 0
			EndIf
		EndIf
		*Struc + SizeOf(Character)
	Wend
	If length : ReadData(FileID, *Var, length) : EndIf
	ProcedureReturn alllength + length
EndProcedure
DataSection
	WriteReadStructure:
		Data.a SizeOf(Ascii), SizeOf(Byte), SizeOf(Character), SizeOf(Double), 0
		Data.a SizeOf(Float), 0, 0, SizeOf(Integer), 0
		Data.a 0, SizeOf(Long), 0, 0, 0
		Data.a 0, SizeOf(Quad), 0, 0, 0
		Data.a SizeOf(Unicode), 0, SizeOf(Word), 0, 0
		Data.a 0
EndDataSection

Var1.Strukt
Var1\Byte = 123
Var1\word = 23432
Var1\Long = 13579123
Var1\Quad = 2468012343344533
Var1\String1 = "Hallo du!"
Var1\Float = 1234.1234
Var1\String2 = "Ich bin NTQ!"
Var1\Double = 123456789.123456789
For a = 0 To 4
	Var1\test1[a]\id = a + 1
	Var1\test1[a]\Name = "Name: " + Str(Random(100000000))
	For b = 0 To 9
		Var1\test1[a]\points[b] = a * 100 + b + 101
	Next
Next

MessageRequester("Struktur Größe", Str(WriteSizeStructure(Var1, @Struc_Strukt)) + " Bytes")

Var2.Strukt
CopyStructure(Var1, Var2, Strukt)

MessageRequester("Kopierte Struktur", "Byte: " + Str(Var2\Byte) + Chr(13) + "Word: " + Str(Var2\word) + Chr(13) + "Long: " + Str(Var2\Long) + Chr(13) + "Quad: " + Str(Var2\Quad) + Chr(13) + "String1: " + Var2\String1 + Chr(13) + "Float: " + Str(Var2\Float) + Chr(13) + "String2: " + Var2\String2 + Chr(13) + "Double: " + StrD(Var2\Double))
For a = 0 To 4
	Debug Var2\test1[a]\id
	Debug Var2\test1[a]\Name
	For b = 0 To 9
		Debug Var2\test1[a]\points[b]
	Next
Next

Debug GetCurrentDirectory()
If CreateFile(0, ".\test_structure.txt")
	WriteStructure(0, Var1, @Struc_Strukt)
	CloseFile(0)
	CompilerSelect #PB_Compiler_OS
		CompilerCase #PB_OS_Linux
			If FileSize("/usr/bin/bless") >= 0
				RunProgram("bless", ".\test_structure.txt", "./", #PB_Program_Wait)
			Else
				MessageRequester("Warnung!", "Hexeditor 'bless' wurde nicht gefunden.")
			EndIf
		CompilerCase #PB_OS_Windows
			RunProgram(".\test_structure.txt", "", "./", #PB_Program_Wait)
	CompilerEndSelect
EndIf

If ReadFile(0, ".\test_structure.txt")
	Var3.Strukt
	ReadStructure(0, Var3, @Struc_Strukt)
	CloseFile(0)
	MessageRequester("Ausgelesene Struktur", "Byte: " + Str(Var3\Byte) + Chr(13) + "Word: " + Str(Var3\word) + Chr(13) + "Long: " + Str(Var3\Long) + Chr(13) + "Quad: " + Str(Var3\Quad) + Chr(13) + "String1: " + Var3\String1 + Chr(13) + "Float: " + Str(Var3\Float) + Chr(13) + "String2: " + Var3\String2 + Chr(13) + "Double: " + StrD(Var3\Double))
	For a = 0 To 4
		Debug Var3\test1[a]\id
		Debug Var3\test1[a]\Name
		For b = 0 To 9
			Debug Var3\test1[a]\points[b]
		Next
	Next
EndIf
Zuletzt geändert von NicTheQuick am 14.08.2007 13:34, insgesamt 4-mal geändert.
Benutzeravatar
Hyper
Beiträge: 194
Registriert: 19.04.2005 19:14

Beitrag von Hyper »

Hallo Nic,

mit diesem Tipp hast Du mir damals schon sehr geholfen. Ich verwende es, um strukturierte Listen aus einem File zu lesen und wieder hineinzuspeichern. Ist auch eine super Sache!

Es ist nur schade, dass es bei größeren Dateien ziemlich lange dauert. Wahrscheinlich, da das Programm ja nun je Einzelfeld ReadData() macht. Könnte man das auch noch irgendwie verallgemeinert mit AllocateMemory o.ä.? Das Pinzip bei mir ist:

Code: Alles auswählen

; Speichern (Lesen ist umgekehrt)
; --------------------------------------

; (File öffen)

; Liste 1 auf Datei speichern
ForEach Liste1()
  WriteStructure(#File, @Liste1(), @Struc1)
Next  

; String1
  WriteStringN(#File, String)  

; Liste 2 auf Datei speichern
ForEach Liste2()
  WriteStructure(#File, @Liste2(), @Struc2)
Next  

; Byte1
WriteByte(#File, Wert)

; ....weiteres

; (File schließen)

PB 5.72
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

Ich hab die Procedures etwas abgeändert und noch eine neue hinzugefügt.
Ein Beispiel ist dabei.

Du kannst ja mal testen, ob es jetzt schneller geht.

Wenn es nicht schnell genug geht, dann liegt es eher an deinem
Festplattencache oder allgemein an deinem Datenträger, auf den du
schreibst.
Benutzeravatar
jear
Beiträge: 288
Registriert: 17.10.2004 01:59
Wohnort: Ammerland

Beitrag von jear »

Die Prozeduren WriteDataStructure() und ReadDataStructure() nach der Methode von NTQ als Include. Die beiden Testprogramme dazu zeigen auch die Anwendung mit Arrays und LinkedLists.

StoreStructuresWithStrings.zip (3KB)

@Hyper
Die von NTQ angewandte Methode bedingt, dass die einzelnen Felder einer Struktur nacheinander abgearbeitet und geschrieben/gelesen werden. Nur so ist es ja möglich, auch Stringfelder direkt mit abzuspeichern.
Strings sind ja in der Struktur tatsächlich nur als Pointer abgelegt. Die Inhalte der Strings stehen in einem besonderen Speicherbereich einer Anwendung. Sie müssen also von dort geholt und beim Lesen wieder dort hingeschrieben werden, was die Routinen sauber und schnell erledigen.
In meiner Version habe ich versucht, noch minimale Geschwindigkeitsvorteile herauszukitzeln. Du kannst ja mal ausprobieren, ob dem so ist.
Man ist nie zu alt zum lernen, auch wenn man dabei manchmal alt aussieht!
Benutzeravatar
Hyper
Beiträge: 194
Registriert: 19.04.2005 19:14

Beitrag von Hyper »

Ja, wie es funktioniert ist schon klar. Ich verwende genau das Prinzip von NTQ in einer Anwendung bei uns in der Firma. Damit liest man über Netzwerk ca. 2 MB große Files, die hauptsächlich aus strukturierten Listen bestehen. Leider dauert das Öffnen deutlich länger, als bspw. MS Word braucht, um eine 2MB-Datei zu öffnen. Das ärgert mich halt.

Ich kuck mir das neue Coding dann an - Danke vorweg...
PB 5.72
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Da will ich den alten Thread mal ausgraben :mrgreen:
ok ... so alt isser auch nicht :lol:

Da ich grad vor dem selben Problem stand, hilft der obige Code schon
mal sehr. ... die einzigste Möglichkeit, allgemeingültig Strukturen in PB
schnell zu kopieren /:->

Da ich auch einen Vergleich von Strukturen brauche, hab ich die Kopier-
Prozedur etwas umgeschrieben. Für alle, die zu faul sind das selber zu
machen und demnächst vor dem selben Problem stehen wie ich:

Code: Alles auswählen

Procedure CompareStructure(*Var1.AllTypes, *Var2.AllTypes, *Struc.Character)
  While *Struc\c 
    Select *Struc\c 
      Case 'b' 
        If *Var2\b <> *Var1\b : ProcedureReturn #False : EndIf 
        *Var1 + SizeOf(Byte) : *Var2 + SizeOf(Byte) 
      Case 'c' 
        If *Var2\c <> *Var1\c : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(Character) : *Var2 + SizeOf(Character) 
      Case 'w'
        If *Var2\w <> *Var1\w : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(Word) : *Var2 + SizeOf(Word) 
      Case 'l' 
        If *Var2\l <> *Var1\l : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(Long) : *Var2 + SizeOf(Long) 
      Case 'f' 
        If *Var2\f <> *Var1\f : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(Float) : *Var2 + SizeOf(Float) 
      Case 'd' 
        If *Var2\d <> *Var1\d : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(Double) : *Var2 + SizeOf(Double) 
      Case 'q' 
        If *Var2\q <> *Var1\q : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(Quad) : *Var2 + SizeOf(Quad) 
      Case 's' 
        If *Var2\s <> *Var1\s : ProcedureReturn #False : EndIf
        *Var1 + SizeOf(String) : *Var2 + SizeOf(String) 
    EndSelect 
    *Struc + SizeOf(Character) 
  Wend 
  ProcedureReturn #True
EndProcedure
MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Beitrag von STARGÅTE »

ich habe mal n frage zu dem CODE.

Vorab erst mal danke, er ist sehr nützich.

Nun zur Frage :

Wie muss man mit Array in der Structure umgehen ?
Structure Waffe
 Art.b
 Zeit.l
EndStructure
Structure T
 x.l
 y.l
 w.f
 Waffe.Waffe[4]
EndStructure

Struc_T.s = "llfblblblblbl"
Müsste man da dann als Struc_T.s = "llfblblblblbl" angeben ? also je nach größe das Arrays und Art die menge an Zeichen ?
Und geht das auch wenn Felder im Array nicht gefüllt sind ? also Lücken drin sind ?
Zuletzt geändert von STARGÅTE am 11.06.2007 22:05, insgesamt 1-mal geändert.
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
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Beitrag von Fluid Byte »

Ich weiss nicht ganz genau was du meinst aber hilft das?

Code: Alles auswählen

Structure Waffe
	Art.b
	Zeit.l[4]
EndStructure

Structure T
	x.l
	y.l
	w.f
	Waffe.Waffe[4]
EndStructure

New_T.T
New_T\x = 120
New_T\y = 250
New_T\w = 30

New_T\Waffe[0]\Art = 1
New_T\Waffe[0]\Zeit[0] = 10
New_T\Waffe[0]\Zeit[1] = 20
New_T\Waffe[0]\Zeit[2] = 30
New_T\Waffe[0]\Zeit[3] = 40

New_T\Waffe[1]\Art = 2
New_T\Waffe[1]\Zeit[0] = 80
New_T\Waffe[1]\Zeit[1] = 150
; ......
Windows 10 Pro, 64-Bit / Outtakes | Derek
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Beitrag von STARGÅTE »

nein.

das ist mir ja klar,

es ging ja um die Proceduren von NicTheQuick

WriteStructure()

dort muss man die Structure als String angeben, und ob das dann mit Arrays in der Structure geht ...

Habe oben die Structure etwas geändert, hatte n fehler drin
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
jear
Beiträge: 288
Registriert: 17.10.2004 01:59
Wohnort: Ammerland

Beitrag von jear »

@STARGÅTE

Eriwansche Antwort: Im Prinzip ja!

Code: Alles auswählen

Struc_T.s = "llf bl bl bl bl bl" 

Code: Alles auswählen

Waffe.Waffe[4]
Waffe.Waffe[4] hat aber nur 4 Mitglieder (0 - 3)! 5 mal "bl" muss also schief gehen!
Man ist nie zu alt zum lernen, auch wenn man dabei manchmal alt aussieht!
Antworten