Write\Read Structure()

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
localmotion34
Enthusiast
Enthusiast
Posts: 665
Joined: Fri Sep 12, 2003 10:40 pm
Location: Tallahassee, Florida

Write\Read Structure()

Post by localmotion34 »

i use structures for a ton of stuff, and it would be nice to be able to just pass the address of a structured variable and have PB write the data to the file. then be able to open the file, and read the structure data and have it reassembeld into a working structured variable.

like:

structure test
long.l
string.s
nested.l[30]
another.q
float.d
endstructure

variable.test

variabe\float=0.90909033
variabe\string=getgadgettext(#gadget_editor)

writestructure(#file_0,location)

the procedure could return 0 for an error, but if successful, return the location that you are now in the file.

now: readstructure(#file_0,location, newvariable.test)

debug newvariabe\string

Code: Select all

!.WHILE status != dwPassedOut
! Invoke AllocateDrink, dwBeerAmount
!MOV Mug, Beer
!Invoke Drink, Mug, dwBeerAmount
!.endw
localmotion34
Enthusiast
Enthusiast
Posts: 665
Joined: Fri Sep 12, 2003 10:40 pm
Location: Tallahassee, Florida

Post by localmotion34 »

no one? not anyone would like to see this? wow, am i out in left field or what here.

Code: Select all

!.WHILE status != dwPassedOut
! Invoke AllocateDrink, dwBeerAmount
!MOV Mug, Beer
!Invoke Drink, Mug, dwBeerAmount
!.endw
Shannara
Addict
Addict
Posts: 1808
Joined: Thu Oct 30, 2003 11:19 pm
Location: Emerald Cove, Unformed

Post by Shannara »

I could of sworn on these forums, someone had a 3.8x(?) version of code that will step through the members of a structure and write them to file, automatically .. much like your doing. But it may have been lost between the many forum post wipes.

I really dont know if it's possible in PB any more. It would definately ease the read/writing to/from files tremendously in PB.
User avatar
Guimauve
Enthusiast
Enthusiast
Posts: 742
Joined: Wed Oct 22, 2003 2:51 am
Location: Canada

Post by Guimauve »

All interpreted language, like MatLAB, can do this.

Personnally I prefer to control how my structure is "Write" and "Read" on file. But I agree this task must be done for each Structure you create.

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structure definition >>>>>

Structure Test
   
   long.l
   string.s
   nested.l[30]
   another.q
   float.d 
   
EndStructure

; <<<<<<<<<<<<<<<<<<<<
; <<<<< Mutators >>>>>

Procedure SetTestlong(*ObjectA.Test, long.l)
   
   *ObjectA\long = long
   
EndProcedure

Procedure SetTeststring(*ObjectA.Test, string.s)
   
   *ObjectA\string = string
   
EndProcedure

Procedure SetTestnested(*ObjectA.Test, Index, Value.l)
   
   *ObjectA\nested[Index] = Value
   
EndProcedure

Procedure SetTestanother(*ObjectA.Test, another.q)
   
   *ObjectA\another = another
   
EndProcedure

Procedure SetTestfloat(*ObjectA.Test, float.d)
   
   *ObjectA\float = float
   
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Observators >>>>>

Procedure.l GetTestlong(*ObjectA.Test)
   
   ProcedureReturn *ObjectA\long
   
EndProcedure

Procedure.s GetTeststring(*ObjectA.Test)
   
   ProcedureReturn *ObjectA\string
   
EndProcedure

Procedure.l GetTestnested(*ObjectA.Test, Index)
   
   ProcedureReturn *ObjectA\nested[Index]
   
EndProcedure

Procedure.q GetTestanother(*ObjectA.Test)
   
   ProcedureReturn *ObjectA\another
   
EndProcedure

Procedure.d GetTestfloat(*ObjectA.Test)
   
   ProcedureReturn *ObjectA\float
   
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Read & Write Binary String <<<<<

Procedure WriteBinaryString(FileID.l, string.s)
  
  Length.l = Len(string)
  WriteLong(FileID, Length) 
  WriteData(FileID, @string, Length) 
  
EndProcedure 

Procedure.s ReadBinaryString(FileID.l)
  
  Length.l = ReadLong(FileID)
  string.s = Space(Length)
  ReadData(FileID, @string, Length) 
  
  ProcedureReturn string
EndProcedure 

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Read Binary file >>>>>

Procedure ReadTest(FileID.l, *ObjectA.Test)
   
   SetTestlong(*ObjectA, ReadLong(FileID))
   SetTeststring(*ObjectA, ReadBinaryString(FileID))
   
   For Index = 0 To 29
      SetTestnested(*ObjectA, Index, ReadLong(FileID))
   Next
   
   SetTestanother(*ObjectA, ReadQuad(FileID))
   SetTestfloat(*ObjectA, ReadDouble(FileID))
   
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Write Binary file >>>>>

Procedure WriteTest(FileID.l, *ObjectA.Test)
   
   WriteLong(FileID, GetTestlong(*ObjectA))
   WriteBinaryString(FileID, GetTeststring(*ObjectA))
   
   For Index = 0 To 29
      WriteLong(FileID, GetTestnested(*ObjectA, Index))
   Next
   
   WriteQuad(FileID, GetTestanother(*ObjectA))
   WriteDouble(FileID, GetTestfloat(*ObjectA))
   
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 31 ms <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Also, if you wish to save a mixed field type structure, like in your exemple, an implicit information must be written to the file (the string lenght). This is why I have created these following procedures.

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Read & Write Binary String <<<<<

Procedure WriteBinaryString(FileID.l, string.s)
  
  Length.l = Len(string)
  WriteLong(FileID, Length) 
  WriteData(FileID, @string, Length) 
  
EndProcedure 

Procedure.s ReadBinaryString(FileID.l)
  
  Length.l = ReadLong(FileID)
  string.s = Space(Length)
  ReadData(FileID, @string, Length) 
  
  ProcedureReturn string
EndProcedure 
Other wise you will not be able to read the string from the file.

Anyways If Fred can achieve the goal to generate a procedure to Write and Read any Structure on binary file it can be greate.

Best Regards

Guimauve
jonljacobi
User
User
Posts: 67
Joined: Mon Jan 16, 2006 10:12 pm

Post by jonljacobi »

I'd love this as well.

Cheers, Jon
Prof
User
User
Posts: 20
Joined: Thu May 17, 2007 11:37 pm

Post by Prof »

I second that. I have already e-mailed this request to Fred.

I think what is required is a GET & PUT command to read and create records in a file. The FileSeek command could be used as an index to the pointer in the file. Or maybe even a RECORDSEEK command to jump from record to record withough having to keep tabs on the file pointer with the Fileseek command.

example....

Code: Select all

Structure Student
    Fornames.s{30}
    Surname.s{25} 
    DOB_DD.s{2}
    DOB_MM.s{2}
    DOB_YY.s{2}
EndStructure

Dim Student_Array.Student(100)

Index=0
Student_Array(0)=Get(FileID,Index)
Would read a structure (one record) from FILEID at point INDEX and load it straight into the structure in the Student_Array at element 0.

This would be really handy to have but would only probably work with fixed length records (which I only use anyway).

I am currently trying to create my own workaround for the time being.
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

For such structure, you can use:

Code: Select all

WriteData(#File, Variable.YourStruct, @SizeOf(YourStruct)
The structure should not contain any regular strings (.s)
User avatar
Guimauve
Enthusiast
Enthusiast
Posts: 742
Joined: Wed Oct 22, 2003 2:51 am
Location: Canada

Post by Guimauve »

Fred wrote:For such structure, you can use:

Code: Select all

WriteData(#File, Variable.YourStruct, @SizeOf(YourStruct)
The structure should not contain any regular strings (.s)
This is true only if you don't need to modify in any following version. For exemple the 1st version of the structure is :

Code: Select all

Structure Student
    Fornames.s{30}
    Surname.s{25}
    DOB_DD.s{2}
    DOB_MM.s{2}
    DOB_YY.s{2}
EndStructure
But the 2nd version of the structure is :

Code: Select all

Structure Student
    Fornames.s{60}
    Surname.s{50}
    DOB_DD.s{6}
    DOB_MM.s{6}
    DOB_YY.s{6}
EndStructure
After this update you will not be able to load the old version anymore with ReadData().

Anyway if you are sure about your structure definition you can use Read/Write Data without any problems. But in my point of view it's not a safe way to save string in a binary file.

If you need to have a regular string (.s) in your stucture the only way to do the gob it's something like this :

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structure declaration <<<<<

Structure Student
  
  Fornames.s
  Surname.s
  DOB_DD.s
  DOB_MM.s
  DOB_YY.s
  
EndStructure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Read binary file operator <<<<<

Procedure ReadStudent(FileID.l, *StudentA.Student)
  
  *StudentA\Fornames = Space(ReadLong(FileID))
  ReadData(FileID, @*StudentA\Fornames, Len(*StudentA\Fornames))
  *StudentA\Surname = Space(ReadLong(FileID))
  ReadData(FileID, @*StudentA\Surname, Len(*StudentA\Surname))
  *StudentA\DOB_DD = Space(ReadLong(FileID))
  ReadData(FileID, @*StudentA\DOB_DD, Len(*StudentA\DOB_DD))
  *StudentA\DOB_MM = Space(ReadLong(FileID))
  ReadData(FileID, @*StudentA\DOB_MM, Len(*StudentA\DOB_MM))
  *StudentA\DOB_YY = Space(ReadLong(FileID))
  ReadData(FileID, @*StudentA\DOB_YY, Len(*StudentA\DOB_YY))
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Write binary file operator <<<<<

Procedure WriteStudent(FileID.l, *StudentA.Student)
  
  WriteLong(FileID, Len(*StudentA\Fornames))
  WriteData(FileID, @*StudentA\Fornames, Len(*StudentA\Fornames))
  WriteLong(FileID, Len(*StudentA\Surname))
  WriteData(FileID, @*StudentA\Surname, Len(*StudentA\Surname))
  WriteLong(FileID, Len(*StudentA\DOB_DD))
  WriteData(FileID, @*StudentA\DOB_DD, Len(*StudentA\DOB_DD))
  WriteLong(FileID, Len(*StudentA\DOB_MM))
  WriteData(FileID, @*StudentA\DOB_MM, Len(*StudentA\DOB_MM))
  WriteLong(FileID, Len(*StudentA\DOB_YY))
  WriteData(FileID, @*StudentA\DOB_YY, Len(*StudentA\DOB_YY))
  
EndProcedure

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Regards
Guimauve
Post Reply