Reading tabled data

BlitzMax Forums/BlitzMax Beginners Area/Reading tabled data

kmac(Posted 2005) [#1]
I've asked this question before, but at that time there wasn't a ready answer. Consequently, I had decided to wait until the full version was released with documentation. However, today after downloading the most recent update I noticed the stdc module, which includes fopen but not fscanf or fprintf. Anyway with some two months having passed, is there an easy way to read tabled data into a struct?


LarsG(Posted 2005) [#2]
what exactly do you mean by tabled data?
perhaps you can give an example of the file you want to read?


kmac(Posted 2005) [#3]
Take the following as an example,

--

smith 34 160
jones 26 176
blake 29 166

---

In C,

#define MAXRECORDS 100

typedef struct
{
char name[30];
int age;
int height;
}DATA;

DATA *users = malloc(MAXRECORDS * sizeof(DATA));

then read the file so that individual records can be easily accessed using a numerical index. I use fscanf in this case and fprintf for the opposite. These functions have made it relatively easy to manage my data in C. As Blitzmax evolves I wouldn't mind migrating.


LarsG(Posted 2005) [#4]
hmmm. I thought someone might have given you a good answer, as I think there might be a clever solution to this..
But from what I can gather, you should be able to do the same thing in 'Max relatively easy.. Say using banks or (RAM)streams...


fredborg(Posted 2005) [#5]
[edit] DOH! Not what you asked :D

Something like this, might work:
Const MAXRECORDS = 100

Type Data
 Field name:String
 Field age:Int
 Field height:Int

 Method WhoIs()
  Print "Name: "+self.name+", Age: "+self.age+", Height: "+self.height+"cm"
 EndMethod
EndType

Local users:Data[MAXRECORDS]
For Local i = 0 Until MAXRECORDS
  users[i] = New Data
  users[i].name   = "Bob "+i
  users[i].age    = Rand(0,100)
  users[i].height = Rand(100,200)
Next

users[10].WhoIs()
users[45].WhoIs()
users[89].WhoIs()
You wouldn't need a max number of records, as you can easily resize the array if you need to.


fredborg(Posted 2005) [#6]
Ok, I assume you will want to read the file, which has certain seperators to show, when a new entry is there?

So something like this should work:
Function ReadDString:String(stream:TStream,seperator:Byte[])
 If stream = Null
  Return ""
 EndIf
 Local b:byte
 Local s:String
 While Eof(stream) = False
  b = ReadByte(stream)
  For Local i = 0 Until seperator.length
   If b = seperator[i]
    Return s
   EndIf
  Next
  s :+ Chr(b)
 Wend
End Function
You then need to open a file, and supply that and a list of seperator values to it...
Something like this:
myfile = ReadFile("hello.txt")
Repeat
 Local s:string = ReadDString(myfile,[Asc(" "),10])
 Print s
Until s = ""
CloseFile(myfile)
It's not tested, so it might be buggy :)


kmac(Posted 2005) [#7]
Thank you for the examples.

I tend to use space separated variables and read until not EOF. I believe that fscanf is flexible in that the number of spaces dosn't matter. Hardcoding the separator would be less tolerant in this case.


PowerPC603(Posted 2005) [#8]
I've created some code for you (with comments):
Type Data
	Global DataArray:Data[]

	Field Name$
	Field Age%
	Field Height%

	Function ReadTabledData(DFH%)
		Local LineFromFile$ = ReadLine$(DFH%) ' Read a line from the given filehandle

		Local a:Data = New Data ' Create a new "Data"-object

		' Store the name without ending spaces
		a.Name$ = LineFromFile$[..10] ' Copy the first 10 characters from the line into field Name$ (0..9)
		' Remove spaces at the end of the name
		While Chr(a.Name$[a.Name$.length - 1]) = Chr(32)
			a.Name$ = a.Name$[..(a.Name$.length - 1)]
		Wend

		a.Age% = Int(LineFromFile$[10..15]) ' Copy the characters 10..14 to the Age-field and convert to Int
		a.Height% = Int(lineFromFile$[15..20]) ' Copy characters 15..19 to the Height-field and convert to Int

		' Resize the array to hold one more object
		DataArray = DataArray[..(DataArray.length + 1)]
		' Add the new Data-object to the array
		DataArray[DataArray.length - 1] = a
	End Function
End Type




' Open a file for reading (DFH = DataFileHandle)
Local DFH% = ReadFile("c:\Test.txt")

' Keep reading until EndOfFile
While Not Eof(DFH%)
	Data.ReadTabledData(DFH%)
Wend

' Close the file
CloseFile(DFH%)



' Print all names and ages
For i = 0 Until Data.DataArray.length
	Print "Name = " + Chr(34) + Data.DataArray[i].Name$ + Chr(34)
	Print "    Age = " + Chr(34) + Data.DataArray[i].Age% + Chr(34)
	Print "    Height = " + Chr(34) + Data.DataArray[i].Height% + Chr(34)
	Print
Next

End


File Test.txt:
Johnny    20   185
Piet      27   170
Geert     26   182
Gert      28   120
Cornelis  50   150