Author Topic: Random Access Files  (Read 10359 times)

0 Members and 1 Guest are viewing this topic.

Offline pinology

  • Newbie
  • Posts: 17
    • View Profile
Random Access Files
« on: August 28, 2018, 03:50:57 pm »
i'm writing a program that keeps stats for my game.  I created a random access file and it worked great for two players when I created the file with the two records manually.  So now I know its only a couple lines but what do I have to add to have it add user input name$ and have it check to see if the name already exists and create a new record if it doesn't

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Random Access Files
« Reply #1 on: August 28, 2018, 04:42:39 pm »
Welcome pinology!

Is this a homework problem?

Well regardless, the first example here:
http://qb64.org/wiki/RANDOM
might be adapted to your very question!

FellippeHeitor

  • Guest
Re: Random Access Files
« Reply #2 on: August 28, 2018, 09:21:00 pm »
Welcome to the forum, pinology.

That does sound like homework.

Can you share what you've already got? We can give tips from that.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Random Access Files
« Reply #3 on: August 28, 2018, 09:39:01 pm »
From my past experience helping folks with this type problem, the point where I find they run into issues is with the string comparisons from length issues...

Often, for random access files, a TYPE is used to store data, which sets strings to a specific length:

TYPE DataType
    FirstName AS STRING * 20
    LastName AS STRING * 20
    Age AS _BYTE
END TYPE

DIM MyFile AS DataType

'Read in a record

INPUT firstname$  'Get the name

IF firstname$ = MyFile.FirstName THEN
    'Do Stuff

***********************

Now, with the above, chances are you'll NEVER find a first name that matches.   

Reason??

The file might have a first name of "Bob" in it, and we might type the name "Bob" into the program, but no match is made....    Why?

MyFile.FirstName is a record of 20 characters, as defined by our TYPE.  It's "Bob                 ", with 17 spaces at the end, instead of just "Bob".

If this *is* your issue, trim the strings to compare first:

IF LTRIM$(RTRIM$(firstname$)) = LTRIM$(RTRIM$(MyFile.FirstName)) THEN
    'Do stuff
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline pinology

  • Newbie
  • Posts: 17
    • View Profile
Re: Random Access Files
« Reply #4 on: August 29, 2018, 12:16:00 am »
cool thanks for the welcomes and responses guys.  Think i'll try smcneills first I feel like a total idiot I must have 100 if then lines in my program but I have been stuck all day forgetting i could just use the dot operator with it to search my file also I probably would have forgot to ltrim and rtrim it too. thanks.  no it isn't homework all it's a game I'm writing, just that I haven't done any programming in about 30 years and have forgotten most of what I knew about random access files and back then I was using gw basic and a little bit of q basic it had just come out. this qb64 was totally new to me but I love it and am getting into programming again. I love the _putimage  and _sndopen stuff, back then I never learned to progam a mouse either took me a couple of weeks to figure all that out.  Actually got most of that done by reading your guys posts but after getting stuck I figured I might as well join and see if I could get an answer and thank you guys so Thanks Guys

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: Random Access Files
« Reply #5 on: August 29, 2018, 12:22:30 am »
Wow, I cut my teeth on GWBasic (Gee-Whiz Basic) too when I moved to the PC platform. QBasic was a god-send. Line numbers ... bleh. :)

Glad to have you as a member pinology ... welcome!
« Last Edit: August 29, 2018, 12:24:20 am by TerryRitchie »
In order to understand recursion, one must first understand recursion.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Random Access Files
« Reply #6 on: August 29, 2018, 12:38:22 am »
From the example I pointed out, I took the part that looked handy and commented it. The function will return the record number for Bob (=search$) if Bob is in a record, 0 if not in record.

Code: QB64: [Select]
  1. FUNCTION RecordPos (file$, search$)
  2.     f = FREEFILE
  3.     OPEN file$ FOR INPUT AS #f
  4.     FL = LOF(f)
  5.     dat$ = INPUT$(FL, f) '<<<<<<<<<<<<<<<<<<<<< this takes the whole file and loads it into one giant string =dat$
  6.     CLOSE f
  7.     recpos = INSTR(dat$, search$) '>>>>>>>>>>>>>>>>>>>>>> this seaches the giant string for Bob, if finds Bob returns the position in string
  8.     IF recpos THEN RecordPos = recpos \ recLEN + 1 ELSE RecordPos = 0 ' >>> this converts the find position in string to a record number for Random access of that record
  9.  
« Last Edit: August 29, 2018, 12:42:11 am by bplus »

Offline pinology

  • Newbie
  • Posts: 17
    • View Profile
Re: Random Access Files
« Reply #7 on: August 30, 2018, 02:23:52 am »
thanks b plus I copied that code I was going to use seek and a counter but that looks way eaiser. line numbers where kinda neat terry if you forgot a line you could just give it a number in the middle

Offline codeguy

  • Forum Regular
  • Posts: 174
    • View Profile
Re: Random Access Files
« Reply #8 on: August 30, 2018, 04:25:08 am »
if your records are in sorted order by what you seek, you can use Binary Search and make things MUCH faster, especially for very large files containing thousands, millions or billions+ records. Also, your drives will thank you.
Code: QB64: [Select]
  1. FUNCTION BinarySearch& (channel%, therecord AS record, search$)
  2.     Lowsearch& = 1 '* this is the lowest valid record number in a random access file
  3.     HighSearch& = LOF(channel%) / LEN(therecord) '* this is the number of records based on UDT length
  4.     DO
  5.         middlesearch& = Lowsearch& + (HighSearch& - Lowsearch&) \ 2
  6.         GET #channel%, middlesearch&, therecord
  7.         IF UCASE$(LTRIM$(RTRIM$(therecord.firstname))) < UCASE$(LTRIM$(RTRIM$(search$))) THEN
  8.             Lowsearch& = middlesearch& + 1
  9.         ELSEIF UCASE$(LTRIM$(RTRIM$(therecord.firstname))) > UCASE$(LTRIM$(RTRIM$(search$))) THEN
  10.             HighSearch& = middlesearch& - 1
  11.         ELSE
  12.             EXIT DO
  13.         END IF
  14.     LOOP until HighSearch& < Lowsearch&
  15.     IF HighSearch& < Lowsearch& THEN
  16.     '* indexes crossed means a match was not found
  17.         BinarySearch& = 0
  18.     ELSE
  19.         BinarySearch& = middlesearch&
  20.     END IF
  21.  
« Last Edit: August 30, 2018, 04:30:22 am by codeguy »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Random Access Files
« Reply #9 on: August 30, 2018, 09:41:55 am »
Hi pinology,

I have to warn with the search method I showed, if people have names like Billybob or Bbbob (the stutterer) those Bobs will be found before real Bob (case insensitive searching), a capital B at start (but that means they go into record like that) and a space after Bob to set search$ to (case sensitive search), will prevent some false positives.

With 2 going on 3 records probably not a problem.




Offline pinology

  • Newbie
  • Posts: 17
    • View Profile
Re: Random Access Files
« Reply #10 on: August 31, 2018, 04:06:00 pm »
thanks for all the help guys i'm kinda stuck here trying to incorporate that function bplus gave me into what I had started so i'll post my code and hopefully someone can lead me in the right direction I feel like an idiot for having to ask again at this point but have only been using qb64 for about a month now but I have a large data file when someone besides me and danny want to play I have to go into it manually and add them to the program


TYPE kstats
    name AS STRING * 10
    ktg AS LONG 'keiths total games
    ktw AS LONG 'keiths total wins
    kgd AS LONG 'keiths games against dan
    kwd AS LONG 'keiths wins against dan
    kab AS LONG 'keiths at bats
    kh AS LONG 'keiths hits
    ka AS DOUBLE 'keiths average

END TYPE
DIM SHARED keith AS kstats
recordlen% = LEN(keith)
keith.name = "Keith": keith.ktg = 0: keith.ktw = 0: keith.kgd = 0: keith.kwd = 0: keith.kab = 0: keith.kh = 0: keith.ka = 0
OPEN "kdata " FOR RANDOM AS #1
PUT #1, 1, keith
CLOSE #1


TYPE dstats
    name AS STRING * 10
    dtg AS LONG 'dans total games
    dtw AS LONG 'dans total wins
    dgk AS LONG 'dans games against keith
    dwk AS LONG 'dans wins against keith
    dab AS LONG 'dans at bats
    dh AS LONG 'dans hits
    da AS DOUBLE 'dans average

END TYPE
DIM SHARED danny AS dstats
recordlen% = LEN(danny)
danny.name = "Danny": danny.dtg = 0: danny.dtw = 0: danny.dgk = 0: danny.dwk = 0: danny.dab = 0: danny.dh = 0: danny.da = 0
OPEN "ddata " FOR RANDOM AS #2
PUT #2, 1, danny
CLOSE #2

'from here down my guesses on how to change what i started as 2 seperate random files into one big data file to be able to add names
'first i only need one type with all the same variables the exception being games against other players but just make a seperate file for that
'i'm kinda getting lost here if i use kdata as the type for each record and players for the big data file i'm thinking it would go something like

'open "players" for random as #1
'get #1,,kdata
'close #1
'then the two players enter their names
'input "Player 1 Enter your name ";player1$
'input "Player 2 Enter your name ";player2$
'kdata.name = player1$
'then i would have to call the recordpos function which i'm not sure how to do and check to see if player1 is already on file and what record number
'and when it gets back call it again to check player2


FUNCTION RecordPos (file$, search$) 'file$ = ddata      search$ = danny.name
    f = FREEFILE
    OPEN file$ FOR INPUT AS #f 'file$ = ddata
    FL = LOF(f)
    dat$ = INPUT$(FL, f) '<<<<<<<<<<<<<<<<<<<<< this takes the whole file and loads it into one giant string =dat$
    CLOSE f
    recpos = INSTR(dat$, search$) '>>>>>>>>>>>>>>>>>>>>>> this seaches the giant string for Bob, if finds Bob returns the position in string
    IF recpos THEN RecordPos = recpos \ recLEN + 1 ELSE RecordPos = 0 ' >>> this converts the find position in string to a record number for Random access of that record
END FUNCTION

Offline pinology

  • Newbie
  • Posts: 17
    • View Profile
Re: Random Access Files
« Reply #11 on: August 31, 2018, 04:39:21 pm »
thanks codeguy I've never worked with binary files but might have to end up giving that a try i'm supposing the the random file can be opened as binary in the function?

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Random Access Files
« Reply #12 on: August 31, 2018, 05:17:42 pm »
Hmm... this is a thinker because what you are tracking is a cross between a database and a spreadsheet, more spread sheet maybe. A spreadsheet would summarize data that a Database might track, total games, totals of anything and with that averages. I was thinking this would be a simple high score tracker.

I always think of a data base tracking info of one event in this case a game between 2 players

Player Game Database Record
   GameDate as string or date, length depends of date format
   OpponentName as string length depends on longest name a player might have
   PlayerAtBats as integer
   PlayerHits as integer     ( _Byte for me :D  )
   PlayerWinTF as _byte

just everything fact you want to track for each player. Databases are recorder's more than they are processors.

In your case, I would advise each game has 2 files to fill out, one record for each player's file (since you are planning a separate file for each player anyway, let's do it from start.)

After database is set up and recording games, then you can run the stats for each player by opening up the file for each player and count things and do averages and write a report.

So now we need a file for each player, file$ = playerName$ + ".DAT"
BUT Heck!
you don't even have to know if a player has a file yet! Because when you open a file for random it is already started!
So with each game open each player's random access ".DAT" from game folder and file the record by adding the new record to the end of the file ie count the records and put the new one in as count + 1.

So pinology, what's you take on what I have said so far?








Offline pinology

  • Newbie
  • Posts: 17
    • View Profile
Re: Random Access Files
« Reply #13 on: August 31, 2018, 05:53:03 pm »
I like it for some reason doing the database by date never occurred to me and like you said can start with the two files already created that will take care of the searching the whole database but I wouldn't want to open a random file for every new player would I? just a record right? then after input of the 2 names in fact that is the only input from the keyboard rest is all mouse if say I had like 50 different players I could just assign the new player the next available file number instead of record number and have that be there id number for the next time they play like if input$ = "joe" then filenum = 48.  I could work that but seems like a lot of files and wouldn't I need 50 if thens to check the previous file numbers for names.  your getting me going there and is that close to what you were trying to say

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Random Access Files
« Reply #14 on: August 31, 2018, 06:03:54 pm »
See if something like this is what you're looking for:

Code: QB64: [Select]
  1. TYPE player
  2.     name AS STRING * 10
  3.     tg AS LONG 'total games
  4.     tw AS LONG 'total wins
  5.     gao AS STRING * 40 'games against others  (up to 10 players)
  6.     wao AS STRING * 40 'wins against others (up to 10 players)
  7.     ab AS LONG 'at bats
  8.     h AS LONG 'hits
  9.     a AS DOUBLE 'average
  10.  
  11. DIM Record AS player, pname AS STRING * 10, temp AS LONG
  12. OPEN "MyDatabase.txt" FOR RANDOM AS #1 LEN = LEN(Record)
  13.  
  14. SCREEN _NEWIMAGE(800, 600, 32)
  15.  
  16.  
  17.     INPUT "Enter Player's Name: ", pname
  18.     IF pname = "" THEN CLOSE: END
  19.     MaxRecord = LOF(1) \ LEN(Record)
  20.     RecordNumber = 0
  21.     FOR i = 1 TO MaxRecord
  22.         GET #1, i, Record
  23.         IF Record.name = pname THEN 'we found the record
  24.             PRINT 'view existing record
  25.             PRINT "RECORD FOUND:"
  26.             PRINT "Player Name: "; Record.name
  27.             PRINT "Total Games: "; Record.tg
  28.             PRINT "Total Wins: "; Record.tg
  29.             FOR x = 1 TO 10
  30.                 PRINT "Games Against Player"; x; ":";
  31.                 PRINT CVL(MID$(Record.gao, 4 * x - 3, 4))
  32.                 PRINT "Wins Against Player"; x; ":";
  33.                 PRINT CVL(MID$(Record.wao, 4 * x - 3, 4))
  34.             NEXT
  35.             PRINT "At Bat: "; player.ab
  36.             PRINT "Hits: "; player.h
  37.             PRINT "Average: "; player.a
  38.             RecordNumber = i
  39.             EXIT FOR 'no need to search any more once we're found
  40.         END IF
  41.     NEXT
  42.     IF RecordNumber = 0 THEN 'we didn't find anyone
  43.         PRINT
  44.         PRINT "NEW RECORD:"
  45.         RecordNumber = MaxRecord + 1
  46.     END IF
  47.     Record.name = pname
  48.     PRINT "Player Name: "; Record.name 'add a new record, or update an old one.
  49.     INPUT "Total Games: ", Record.tg
  50.     INPUT "Total Wins: ", Record.tg
  51.     FOR x = 1 TO 10
  52.         PRINT "Games Against Player"; x; ":";
  53.         INPUT "", temp
  54.         MID$(Record.gao, 4 * x - 3, 4) = MKL$(temp)
  55.         PRINT "Wins Against Player"; x; ":";
  56.         INPUT "", temp
  57.         MID$(Record.wao, 4 * x - 3, 4) = MKL$(temp)
  58.     NEXT
  59.     INPUT "At Bat: ", player.ab
  60.     INPUT "Hits: ", player.h
  61.     INPUT "Average: ", player.a
  62.     PUT #1, RecordNumber, Record

Note, this will always ask you to update a record once it finds it, but it should be able to handle finding  and returning files automatically, as well as creating new ones when it doesn't find them.

Note 2: This also has a limited data set for up to 10 players to be tracked at once.  You'd need to tweak the string size to set it for the max number of players you want to track.

Note 3: If I was going to seriously use something like this, I'd change it so that the INPUT used a basic menu system so the user could update the fields they wanted, without having to reenter everything each time they viewed a record. 

But, as a little demo, this might be able to help you on the road to being able to do what you were talking about.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!