Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Indexed files
#1
I need to create a file that I can access randomly, by specifying a record-number
I think this requires an Indexed file, but don't know how to create this. 
I would need to access it with something like 
Open "myfile" for input as #1
Input #1, RecNum10 (to access the 10th record).
I can do this by reading from the file 10 times, but this would be slow.
Any suggestions?
Of all the places on Earth, and all the planets in the Universe, I'd rather live here (Perth, W.A.) Big Grin
Please visit my Website at: http://oldendayskids.blogspot.com/
Reply
#2
I think you can use GET and PUT with RANDOM files to accomplish what you want.
Reply
#3
You want to open a file for Random (Access) and best if you set it with a fixed length record size. This allows you to read and write records to a record number.

Here is a demo for johnno for tracking Blood tests;
Code: (Select All)
'Tutorial (with help from Wiki):

' save this bas file in the folder where you want you BloodData.dat file to go first!

Type Blood
    date As String * 10
    level As Integer
    comment As String * 50
End Type

'Make a record:
Dim Shared Record As Blood, lenRecord&, nRecs&
lenRecord& = Len(Record)

' start up the data file
Open "BloodData.dat" For Random As #1 Len = lenRecord&


Record.date = "2022/11/18"
Record.level = 101
Record.comment = "This is my comment."

'Store a record:
Put #1, 1, Record

nRecs = LOF(1) / lenRecord&
Print "number recs:"; nRecs ' good 1 lets get it

' get record
Get #1, 1, Record
Print Record.date; ":"; Record.level; ", "; Record.comment

' stick another record in there
nRecs = nRecs + 1
Record.date = "2022/11/19"
Record.level = 105
Record.comment = "This is my next comment."
Put #1, nRecs, Record

' check records
nRecs = LOF(1) / lenRecord&
Print "number recs:"; nRecs ' good 1 lets get it

For i = 1 To nRecs
    Get #1, i, Record
    Print Record.date; ":"; Record.level; ", "; Record.comment
Next

You create a data entry form for entering the record to file file or reading records out.
b = b + ...
Reply
#4
Here's another demo, it's loading a Random Access file with "fake" data for practice reading and writing.
Code: (Select All)
_Title "UDT to Random Access File Test" ' b+ 2021-12-24
Type Image
    As String * 255 Text, FileName
    As Long SzX, SzY, PosX, PosY
End Type

Const SW = 1000, SH = 700
ReDim As Image TheItem(1 To 100), TheRecord

TheItem(1).Text = FakeText$(255)
TheItem(1).FileName = FakeText$(255)
Print TheItem(1).Text
Print Len(TheItem(1).Text)
Print TheItem(1).FileName
Print Len(TheItem(1).FileName)
Print "zzz"
Sleep

'make fake data to file
For i = 1 To 100
    TheItem(i).Text = FakeText$(255)
    TheItem(i).SzX = 100 + Rnd * 20
    TheItem(i).SzY = 70 + Rnd * 14
    TheItem(i).PosX = Rnd * (SW - TheItem(i).SzX)
    TheItem(i).PosY = Rnd * (SH - TheItem(i).SzY)
    TheItem(i).FileName = FakeText$(255)
Next

Open "Data Dump.RA" For Random As #1 Len = Len(TheRecord)
For i = 1 To 100
    'odious and tedious is this
    TheRecord.Text = TheItem(i).Text
    TheRecord.SzX = TheItem(i).SzX
    TheRecord.SzY = TheItem(i).SzY
    TheRecord.PosX = TheItem(i).PosX
    TheRecord.PosY = TheItem(i).PosY
    TheRecord.FileName = TheItem(i).FileName
    Put #1, , TheRecord
Next
Close #1
Print "Data File Ready"

' OK we got data filed! Now can we get it back
Open "Data Dump.RA" For Random As #1 Len = Len(TheRecord)
For i = 1 To 100
    Cls
    Get #1, i, TheRecord
    Print "Record Number:"; i
    Print "Text: "; TheRecord.Text
    Print "SzX:"; TheRecord.SzX
    Print "SzY:"; TheRecord.SzY
    Print "PosX:"; TheRecord.PosX
    Print "PosY:"; TheRecord.PosY
    Print "FileName:"; TheRecord.FileName
    Print " zzz..."
    Sleep
Next
Close #1

Function FakeText$ (lengthh)
    BlankString$ = Space$(lengthh)
    fini = Int(Rnd * 255) + 1
    For i = 1 To fini
        Mid$(BlankString$, i, 1) = Chr$(Rnd * (96 - 32) + 32)
    Next
    FakeText$ = BlankString$
End Function
b = b + ...
Reply
#5
I need to add this to the tutorial. I explain sequential flat files but just mention at the end of the lesson that records can be done with RANDOM. I just created a screen sticky note to remind me to do this.

Update:

Oh yuck:

Code: (Select All)
TYPE Image
    AS STRING * 255 Text, FileName
    AS LONG SzX, SzY, PosX, PosY
END TYPE
I just saw this above. Didn't know that this was possible inside of a UDT structure but it looks rather messy in my opinion. No room for comments.

Code: (Select All)
TYPE Image
    Text AS STRING * 255 ' comments here
    FileName AS STRING * 255 ' comments here
    SzX AS LONG ' comments here
    SzY AS LONG ' more comments
    PosX AS LONG ' the rain in Spain
    PosY AS LONG ' falls mainly on the plain
END TYPE
Ah, much better.
New to QB64pe? Visit the QB64 tutorial to get started.
QB64 Tutorial
Reply
#6
@PhilOfPerth have you worked with fixed length strings before?

Very important in random access files to know about fixed length strings. They help keep you record size the same size for all records with strings in them.
b = b + ...
Reply
#7
I used to do this stuff for work with GW Basic and QB4.5, parts, mold and die inventorys.
b = b + ...
Reply
#8
(11-03-2023, 11:55 PM)bplus Wrote: @PhilOfPerth have you worked with fixed length strings before?

Very important in random access files to know about fixed length strings. They help keep you record size the same size for all records with strings in them.

Here's an example of a variable length, indexed filesystem at work:  https://qb64phoenix.com/forum/showthread.php?tid=142

Fixed length sizes help, but they're not a necessity.  Wink
Reply
#9
I think there is a better way to do this using the SEEK command.

You can do this and go directly here, circumventing all of the random using BINARY symbols instead:

file$ = "c:\myfile.dat"

OPEN FILE$ for BINARY as #1

Z$= CHR$ (0) ' Null value, Gets 1 symbol at a time.

SEEK #1,10 : 'Seek out the 10th symbol of the file.

GET #1 , , Z$: 'Get the symbol.
 
Print Z$: 'Prints the symbol

CLOSE #1


You can do this too in GWBASIC and Qbasic both, and around the time of GWBASIC, you were limited to a FIELD length of about 128 characters or less...

But this is the way to access files efficiently at a byte level (unless you have another more recent way using file direct access).
Reply
#10
(11-04-2023, 01:20 AM)JamesAlexander Wrote: I think there is a better way to do this using the SEEK command.

You can do this and go directly here, circumventing all of the random using BINARY symbols instead:

file$ = "c:\myfile.dat"

OPEN FILE$ for BINARY as #1

Z$= CHR$ (0) ' Null value, Gets 1 symbol at a time.

SEEK #1,10 : 'Seek out the 10th symbol of the file.

GET #1 , , Z$: 'Get the symbol.
 
Print Z$: 'Prints the symbol

CLOSE #1


You can do this too in GWBASIC and Qbasic both, and around the time of GWBASIC, you were limited to a FIELD length of about 128 characters or less...

But this is the way to access files efficiently at a byte level (unless you have another more recent way using file direct access).

Thanks James. I'll experiment with this. The records are of different lengths, so I may need to use fixed-length strings as also suggested.
Of all the places on Earth, and all the planets in the Universe, I'd rather live here (Perth, W.A.) Big Grin
Please visit my Website at: http://oldendayskids.blogspot.com/
Reply




Users browsing this thread: 3 Guest(s)