Author Topic: Random Access File Question  (Read 4016 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Random Access File Question
« on: January 06, 2021, 12:23:38 am »
So, if we try the following code, we get an error:

Code: QB64: [Select]
  1. OPEN "temp.txt" FOR RANDOM AS #1 LEN = 25
  2. temp$ = SPACE$(25)
  3. PUT #1, 1, temp$
  4.  

Now, if we try the following, the same style program works perfectly fine:

Code: QB64: [Select]
  1. DIM temp AS STRING * 25
  2. OPEN "temp.txt" FOR RANDOM AS #1 LEN = 25
  3. PUT #1, 1, temp
  4.  

The problem here is that temp$, in the first example, is a variable length string, so PUT wants to put a 2-byte header to the data to store our length, giving us a total length of 27.

My question is this one:  Is this the same style behavior as QB45?  I always thought that if we specified the length of the random access file in OPEN, that we'd use that value to calculate our file pointer position to GET and PUT data.  I know RANDOM access allows for variable length data to be used, but I always thought that was only if we didn't specifically set a random length in the OPEN statement.



It's been so long since I last used RANDOM access, I think I'm just going to end up forgetting that it even exists for my own code.  It just seems so much simpler to use BINARY and manually calculate that offset:

Code: QB64: [Select]
  1. OPEN "temp.txt" FOR BINARY AS #1
  2. temp$ = SPACE$(25)
  3. PUT #1, recordnum * 25 + 1, temp$
  4.  
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: Random Access File Question
« Reply #1 on: January 06, 2021, 05:00:07 am »
This behaviour is consistent with QB45.

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Random Access File Question
« Reply #2 on: January 06, 2021, 03:54:46 pm »
I'm a random kind of coder, a slave to the LEN statement. I've never understood BINARY files. How do you determine how long a record is, and/or write the data to the right position? I guess I'm the proverbial fish that doesn't understand the concept of "wet vs. dry".

Perhaps someone could point me to some examples. I'm definitely interested, if it's as easy as it sounds.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Random Access File Question
« Reply #3 on: January 06, 2021, 04:19:18 pm »
I'm a random kind of coder, a slave to the LEN statement. I've never understood BINARY files. How do you determine how long a record is, and/or write the data to the right position? I guess I'm the proverbial fish that doesn't understand the concept of "wet vs. dry".

Perhaps someone could point me to some examples. I'm definitely interested, if it's as easy as it sounds.

If you're used to RANDOM access files, you can convert all of those over to BINARY with almost no effort, as illustrated below:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(120, 40, 0)
  2.  
  3. DIM info AS STRING * 100, inpt AS STRING * 100 'one string to write data, one string to read data.
  4.  
  5. OPEN "temp.txt" FOR RANDOM AS #1 LEN = 100 'let's open the file first for random
  6. info = "foo"
  7. PUT #1, 1, info 'and put 3 pieces of data in it, in record #1
  8. info = "poo"
  9. PUT #1, 2, info 'in record #2
  10. info = "shoo a shoo a boo"
  11. PUT #1, 3, info 'in record #3
  12.  
  13. GET #1, 1, inpt 'And let's get and print those records, starting at #1
  14. PRINT inpt
  15. GET #1, 2, inpt 'record #2
  16. PRINT inpt
  17. GET #1, 3, inpt 'record #3
  18. PRINT inpt
  19.  
  20.  
  21. OPEN "temp.txt" FOR BINARY AS #1 'Now, to show that BINARY does the exact same thing...
  22. filesize = 100
  23. record_number = 1
  24. GET #1, filesize * (record_number - 1) + 1, inpt 'And let's get and print those records, starting at #1
  25. PRINT inpt
  26. record_number = 2
  27. GET #1, filesize * (record_number - 1) + 1, inpt '#2
  28. PRINT inpt
  29. record_number = 3
  30. GET #1, filesize * (record_number - 1) + 1, inpt '#3
  31. PRINT inpt
  32.  
  33. 'now, that might seem a little complex from the above, but that's only because I wanted to show the
  34. 'formula which you'd use to calculate the offset of each file's position.
  35. 'usually, you'd use something much simpler like the following:
  36.  
  37. info = "Potato Chips"
  38. PUT #1, 1, info
  39. info = "Root Beer"
  40. PUT #1, 101, info
  41. info = "Cheese"
  42. PUT #1, 201, info
  43.  
  44.  
  45. 'And to show that our data is still interchangeable, I'm going to use RANDOM access to get and print it
  46.  
  47. OPEN "temp.txt" FOR RANDOM AS #1 LEN = 100 'let's open the file first for random
  48.  
  49. GET #1, 1, inpt 'And let's get and print those records, starting at #1
  50. PRINT inpt
  51. GET #1, 2, inpt 'record #2
  52. PRINT inpt
  53. GET #1, 3, inpt 'record #3
  54. PRINT inpt
  55.  
  56. 'So, as you can see, BINARY and RANDOM really aren't that different.  The only real change is that
  57. 'you have to calculate the offset yourself, rather than just reference a record number.
  58. 'And that caculation is rather simple:
  59. '(Recordnumber - 1) * RecordLength + 1
  60.  
  61. 'Our first record always starts at Byte 1...
  62. 'Every record after that increases by the record length in size.  (in this case 100)
  63. 'so 001, 101, 201, 301, 401, 501, 601 are the positions of the first 7 records.
  64.  

https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Random Access File Question
« Reply #4 on: January 06, 2021, 04:45:08 pm »
And here's another simple example which showcases how easy it is to make use of BINARY mode:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(640, 480, 256)
  2.  
  3. 'let's create a random color pattern on our screen
  4. FOR x = 0 TO 639 STEP 40
  5.     FOR y = 0 TO 479 STEP 40
  6.         LINE (x, y)-STEP(40, 40), RND * 256, BF
  7.     NEXT
  8.  
  9. 'And now, using binary, we're going to store that image to the drive, so we can use it later.
  10. x = _WIDTH: y = _HEIGHT
  11. OPEN "temp.txt" FOR BINARY AS #1
  12. PUT #1, , x
  13. PUT #1, , y
  14. FOR y = 0 TO 479
  15.     FOR x = 0 TO 639
  16.         b = POINT(x, y)
  17.         PUT #1, , b
  18.     NEXT
  19. SLEEP 'Study the image for a few moments so you can identify it when we restore it
  20.  
  21. x = 0: y = 0: b = 0 'I'll reset our data so you can see we're getting it all fresh
  22.  
  23.  
  24. OPEN "temp.txt" FOR BINARY AS #1
  25. GET #1, , x
  26. GET #1, , y
  27. SCREEN _NEWIMAGE(x, y, 256) 'notice that I stored the screen size, so I'm creating a brand new screen from scratch
  28. CLS 'and just to prove this is a clear screen
  29.  
  30. FOR y = 0 TO 479
  31.     FOR x = 0 TO 639
  32.         GET #1, , b
  33.         PSET (x, y), b
  34.     NEXT
  35.  
  36.  
  37. 'And if you notice in this demo, I didn't bother to manually jump between any data in our file -- I left the
  38. 'second parameter blank in all cases, which tells BINARY GET/PUT to simply get/put the next bit of information
  39. 'at the next byte of the file, where we left off from accessing it last.
  40.  
  41. 'Since I wrote it as Width, Height, color point, color point, color point..., all I had to do is read it back
  42. 'in the same order for it to make sense and be viable data for my program.
  43.  

Now, try and sort out how the heck to write that data while in RANDOM access mode.  ;D

It's simple enough to convert from RANDOM access to BINARY, but it can be almost impossible to go in the other direction.  BINARY is much more powerful and much more flexible than RANDOM access ever could be.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Random Access File Question
« Reply #5 on: January 06, 2021, 04:53:49 pm »
In QB45, as with QB64, I would add those 2 bytes to any RA file, and it would work, without making a fixed length string destination. The thing is, if you open the file in Notepad, it won't be readable in a neatly organized fashion unless you use the fixed string method.

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Random Access File Question
« Reply #6 on: January 06, 2021, 11:29:08 pm »
Thanks for the examples Steve. That gives me something I can wrap my head around. So if I'm understanding this right, it can work with variable length data so long as that data is read back in the proper sequence, since it retains a pointer off the last PUT/GET operation. I'm interested in getting multiple user defined type array stuff consolidated into fewer files.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Random Access File Question
« Reply #7 on: January 06, 2021, 11:59:55 pm »
Thanks for the examples Steve. That gives me something I can wrap my head around. So if I'm understanding this right, it can work with variable length data so long as that data is read back in the proper sequence, since it retains a pointer off the last PUT/GET operation. I'm interested in getting multiple user defined type array stuff consolidated into fewer files.

For variable length data, the easiest implementation is to write LENGTH, then DATA.

OPEN "Temp.txt" FOR BINARY AS #1
A$ = "Whatever you want, of any length."
L = LEN(A$)
PUT #1, , L 'put the length
PUT #1, , A$ 'put the data
CLOSE

Then when you want to read it back, you use that information to get what you need:

OPEN "Temp.txt" FOR BINARY AS #1
GET #1, , L 'get the length
A$ = SPACE$(L) 'set a blank string to the length of the data you want to retrieve
GET #1, , A$ 'get the data
CLOSE



Without any values in the 2nd parameter of a GET statement, the command will get from the next byte in an open file, telling it where to START getting data, and the length of the data type in the 3rd parameter tells it HOW MUCH data to retrieve.

GET #1, , x% <---  Get from the next byte of the file, 2 bytes (an integer)
GET #1, , Y&  <---  Get from the next byte of the file, 4 bytes (a long)
GET #1, , A$  <---  Get from the next byte of the file, whatever the  LEN(A$) is.

If A$ = "", then you'll get nothing from the file.  With fixed-length strings, it's not something you have to worry about as the size is always set (DIM A AS STRING * 3), but it does matter for variable-length data.



User Defined Types generally work perfect with BINARY mode, as they have a permanent, set size.

TYPE Foo
    bar AS INTEGER
    cheese_fingers AS STRING * 10
    wang AS LONG
END TYPE

DIM example AS Foo

Just by looking at the above, you can tell every record is going to be a set length of 16 bytes (2 for the integer, 10 for the string, 4 for the long).  All you'd have to do with it is:

GET #1, , example 'or PUT

It's only when dealing with variable-width data that you need to store the length with it.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline OldMoses

  • Seasoned Forum Regular
  • Posts: 469
    • View Profile
Re: Random Access File Question
« Reply #8 on: January 07, 2021, 07:46:37 am »
So I tried the following and it worked perfectly, I see now why you like this approach. In my space flight program, I'm storing ship data in multiple UDTs, that are indexed to each other, and storing each in a separate random access file. This would help me clean up a lot of file clutter, and reams of file ops in the program itself.

I learned to work with _MEM from your videos, but wasn't picking up on the similarities because I had never worked with BINARY file ops. I can see the correlation now. Thanks.

Code: QB64: [Select]
  1. TYPE C3
  2.     i AS INTEGER
  3.     j AS INTEGER
  4.     k AS INTEGER
  5.  
  6. TYPE C5
  7.     m AS _BYTE
  8.     n AS _BYTE
  9.     o AS _BYTE
  10.     p AS _BYTE
  11.     q AS _BYTE
  12.  
  13. DIM three(200) AS C3
  14. DIM five(200) AS C5
  15.  
  16. 'comment this block when reading
  17. FOR x = 1 TO 10
  18.     three(x).i = x * 1: three(x).j = x * 2: three(x).k = x * 3
  19.     five(x).m = x * 1: five(x).n = x * 2: five(x).o = x * 3: five(x).p = x * 4: five(x).q = x * 5
  20.     PRINT three(x).i, three(x).j, three(x).k
  21.     PRINT five(x).m, five(x).n, five(x).o, five(x).p, five(x).q
  22. x = x - 1
  23. OPEN "smash.txt" FOR BINARY AS #1
  24. PUT #1, , x
  25. FOR y = 1 TO x
  26.     PUT #1, , three(y)
  27.     PUT #1, , five(y)
  28.  
  29.  
  30. 'comment this block when writing
  31. OPEN "smash.txt" FOR BINARY AS #1
  32. GET #1, , x
  33. FOR y = 1 TO x
  34.     GET #1, , three(y)
  35.     GET #1, , five(y)
  36.     'GET #1, , three(y)  'what happens when we screw the order up?
  37.  
  38. FOR z = 1 TO x
  39.     PRINT three(z).i, three(z).j, three(z).k
  40.     PRINT five(z).m, five(z).n, five(z).o, five(z).p, five(z).q

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Random Access File Question
« Reply #9 on: January 07, 2021, 12:37:31 pm »
I think if I had QB64, back in the day I was programming apps for my office, I would have used BINARY instead of RANDOM for all my database work. I recall reading all the RANDOM BS in the QBasic texts back then, as to how to set up RA files, and I decided to try something rebel, instead, which was to determine the longest string in any database, and then setting the LEN statement to that length + 2. It worked for every database I had, and I never had fixed length data entries in any database I created. The only drawback was the different data length entries made the files look like a complete mess when I opened them in Notepad.

Pete

Your friendly neighborhood Screen Zero Hero.
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline Juanjogomez

  • Forum Regular
  • Posts: 117
    • View Profile
Re: Random Access File Question
« Reply #10 on: January 07, 2021, 12:58:50 pm »
I have used random access files for many years. You always have to define the length of the fields before accessing them and to assign them a value you have to use LSET.
Another option is to define the variables with TYPE

For the example you put, it would be ...

Code: QB64: [Select]
  1. OPEN "temp.txt" FOR RANDOM AS #1
  2. FIELD #1, 25 as temp$
  3. LSET temp$ = "Anything"
  4. PUT #1, 1
  5.  
  6. OPEN "temp.txt" FOR RANDOM AS #1
  7. FIELD #1, 25 as temp$
  8. get #1, 1
  9. print temps$
  10.  

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: Random Access File Question
« Reply #11 on: January 07, 2021, 01:14:14 pm »
I have used random access files for many years. You always have to define the length of the fields before accessing them and to assign them a value you have to use LSET.
Another option is to define the variables with TYPE

For the example you put, it would be ...

Code: QB64: [Select]
  1. OPEN "temp.txt" FOR RANDOM AS #1
  2. FIELD #1, 25 as temp$
  3. LSET temp$ = "Anything"
  4. PUT #1, 1
  5.  
  6. OPEN "temp.txt" FOR RANDOM AS #1
  7. FIELD #1, 25 as temp$
  8. get #1, 1
  9. print temps$
  10.  

No, it isn't the only way, but yes, your example is the oldest method. At some point, authors moved past that to use DIM and LEN...

DIM a as string * 80
a = "Screen Zero Hero"
OPEN "tmp.tmp" FOR RANDOM AS #1 LEN = 80
PUT #1, 1, a
CLOSE #1

But then I discovered this back in 1990 something...

Code: QB64: [Select]
  1.  ' First the "revised" text book method ...
  2. DIM a AS STRING * 80
  3. a = "Screen Zero Hero"
  4. OPEN "tmp.tmp" FOR RANDOM AS #1 LEN = 80
  5. PUT #1, 1, a
  6. a = ""
  7. OPEN "tmp.tmp" FOR RANDOM AS #1 LEN = 80
  8. GET #1, 1, a
  9. ' Now let's do it my rebel way!
  10. x$ = "Screen Zero Hero"
  11. raplus2% = LEN(x$) + 2
  12. KILL "tmp.tmp"
  13. OPEN "tmp.tmp" FOR RANDOM AS #1 LEN = raplus2%
  14. PUT #1, 1, x$
  15. OPEN "tmp.tmp" FOR RANDOM AS #1 LEN = raplus2%
  16. GET #1, 1, Pete$
  17. PRINT Pete$

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/