home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / RELATIVELY-EASY-PART-1 < prev    next >
Encoding:
Text File  |  2019-04-13  |  17.1 KB  |  366 lines

  1. RELATIVE FILES MADE RELATIVELY EASY Part 1
  2.  
  3. by Bill Brier
  4.  
  5.  
  6. This is part one of a three part article explaining the use of RELative
  7. files in database programs.
  8.  
  9. The article assumes some familiarity with BASIC and the disk drive.
  10.  
  11.  
  12.  
  13.  
  14.  
  15. I.  WHAT IS A RELATIVE FILE?
  16.  
  17. At one time or another you have probably used a commercial database program
  18. to store and retrieve information on a disk.  And you have probably looked
  19. at the file disk directory on at least one occasion and saw a file entry
  20. marked with REL.  You knew that the RELative file was the backbone of the
  21. whole database but it seemed like a mysterious, unfathomable storage
  22. structure.  Well, this article will help you to understand and use RELative
  23. files and will explain some easy-to-use techniques for creating your own
  24. RELative file database programs.
  25.  
  26. A RELative file is a special type of SEQuential disk file that is
  27. structured into logical data units called RECORDS.  Each record is assigned
  28. a number.  These records, in turn, are structured into data units called
  29. FIELDS.  Each field is assigned a numerical position within the record.
  30. Because of this unique structure, the RELative file allows the programmer to
  31. access just one record and, if desired, one field of that record.  This
  32. feature makes the design of fast searching databases easy and efficient.
  33.  
  34. A RELative file is the direct descendant of an older and more complex filing
  35. system refered to as DIRECT-ACCESS or RANDOM ACCESS.  Such a system required
  36. that the program manage the use of the tracks and sectors on the disk, a
  37. complex and somewhat inefficient task.  When using RELative files, the disk
  38. operating system (DOS) handles the track and sector usage and relieves the
  39. program of this task.  The Commodore DOS does all of the "dirty work" for
  40. you, leaving you with a less daunting task to accomplish.
  41.  
  42. Because of the random access nature of a RELative file a bit of planning is
  43. required on the programmer's part.  Let's take a look at this aspect first.
  44. The rest will then become considerably easier to understand.
  45.  
  46. II.  RECORDS and FIELDS
  47.  
  48. The job of any RELative file is to store information in a form that can be
  49. readily used some time in the future.  To do this, the file must be properly
  50. structured to avoid a confusing mess.  Thus, the individual fields and their
  51. use within a record must be understood.  We will use a mailing list as a
  52. simple and easily understood example.
  53.  
  54. A record in our mailing list would consist of the following fields:
  55.  
  56.     First/last name
  57.     Street address
  58.     City
  59.     State
  60.     Zip code
  61.     Area code
  62.     Telephone number
  63.  
  64. The reason for separating these items into individual fields is so that the
  65. program will be able to search on them.  For example, the user may have a
  66. need to search out certain area codes.  By having the area code in a
  67. separate field such a search can be easily and quickly performed.
  68.  
  69. Once you have defined the fields that you wish to use you must next
  70. determine the number of characters each field will be allowed to contain.
  71. It is in this area that many beginners encounter difficulty.  Here are
  72. representative lengths for the above fields:
  73.  
  74.     First/last name: 33
  75.     Street address:  26
  76.     City:            19
  77.     State:            3
  78.     Zip code:         6
  79.     Area code:        4
  80.     Telephone number: 9
  81.  
  82. Before you start to write your program construct a table just like the one
  83. above and work out the field lengths.  Then, add one extra character to each
  84. field.  This extra character will be the field DELIMITER (normally a
  85. carriage return).  The delimiter is the means by which your program will be
  86. able to determine where one field ends and the next one starts.  The above
  87. table includes a delimiter in each length.  Now, add up all of the lengths.
  88. If you've copied and added up my table you should arrive with 100 as a
  89. total.  This is the RECORD LENGTH in bytes.  A record may not exceed 254
  90. bytes total length including the delimiters.  If your program must use a
  91. greater length then you will have to use a second record to store the
  92. information that will not fit into the first one.  Avoid this type of
  93. situation if at all possible.
  94.  
  95. With the field size and structure determined you now need to determine the
  96. number of records that your file should contain.  You should be realistic
  97. about this as there is no point in structuring a file much larger than what
  98. is needed to do the job.  Also, there is only so much storage space on a
  99. disk and room must be left for other uses which will be discussed later.
  100.  
  101. In our mailing list program we will create a file with 500 records.  To
  102. determine the approximate number of bytes that this file will require and
  103. the number of disk sectors (blocks) it will use multiply the record length
  104. in bytes by the number of records used and add five percent to this total.
  105. That will give you the storage usage of the RELative file in bytes.  Divide
  106. that usage by 256 to get the blocks used.  In our example file, we would
  107. figure as follows:
  108.  
  109.     100 * 500 + 5% = 52500 bytes
  110.     52500 / 256 = 205 blocks used
  111.  
  112. The five percent add-on covers disk overhead needed to maintain the RELative
  113. file.  This overhead is called the SIDE-SECTOR CHAIN OF BLOCKS.  It is an
  114. internal "roadmap" maintained on the disk by the drive so it can find the
  115. various records without a lot of lost time.  Since an empty 1541-formatted
  116. disk has 664 free blocks, we know that our file will fit without any
  117. problem.
  118.  
  119. We need to do two other things and then we'll be ready to start programming.
  120. Each of the fields needs to have a POSITION number and a string variable
  121. assigned to it.  The position or P number tells the drive which field in the
  122. record that the data is to be located.  The string variable obviously will
  123. be used to store the data.  The P number is calculated by adding the length
  124. of a field to its starting position in the record, the result which becomes
  125. the P number for the next field.  Using our already defined fields, let's
  126. set up a table listing the assigned variables and P numbers:
  127.  
  128.     Field                Len.  Var.   P
  129.     ===================================
  130.     1st/last name        33    NA$    1
  131.     Street address       26    AD$   34
  132.     City                 19    CT$   60
  133.     State                 3    SA$   79
  134.     Zip code              6    ZI$   82
  135.     Area Code             4    AC$   88
  136.     Telephone number      9    TN$   92
  137.     ===================================
  138.  
  139. If you study this table for a while a pattern will emerge.  The P number for
  140. a given field (except the first one of course) is the length of the previous
  141. field plus the P number for that previous field.  For example, the P number
  142. for the address field (AD$) is 33 (the length of NA$) plus 1 (the field
  143. position of NA$) or 34.  The record always starts at position or byte number
  144. 1 and works up from there.  Note that in calculating the P number the length
  145. of the field also includes the extra character for the delimiter.  If you
  146. neglect to leave room for it two fields may run together and create a mess.
  147.  
  148. But, you say, wait a minute!  The name or city or address lengths will vary
  149. depending on the actual information entered by the user.  Yes, that is true
  150. but is nothing to be concerned about.  If your data string is shorter than
  151. the maximum length allowed by the field the unused portion of the field will
  152. simply be along for the ride.  The delimiter can be at any point within the
  153. field.  The only thing you must be careful of is to avoid a data string
  154. length greater than the field length.  Such an occurence would result in two
  155. fields running together or an overflow in the record.
  156.  
  157. III.  CREATING A NEW RELATIVE FILE
  158.  
  159. When your program first starts out there won't be a RELative file to work
  160. with.  Therefore you must create one on the file disk.  Unlike SEQuential
  161. files, RELative files make no distinction between reading and writing to a
  162. file.  A RELative file can be OPENed, written to, read from and CLOSEd
  163. without any special gymnastics on the part of your program.  You do need to
  164. create a file full of "dummy" records to start with, though.  This file
  165. initially won't contain any data but will contain all of the records you
  166. will need to store your data in.  Before we create our new file however we
  167. must become acquainted with some programming techniques required to
  168. manipulate a RELative file.
  169.  
  170. When reading or writing a RELative file you must have two disk files
  171. simultaneously OPENed.  One of them will, of course, be the file associated
  172. with the RELative file.  The other will be for communications on the error
  173. channel (channel 15).  For our purposes we will use file #1 for the error
  174. channel and file #2 for the RELative file itself.  The data being stored or
  175. retrieved in the RELative file will be passed using file #2 and DOS error
  176. messages and commands will be passed using file #1.  Also, you must always
  177. OPEN the error channel first.  With that settled, let's OPEN our new file:
  178.  
  179.     OPEN1,8,15:OPEN2,8,2,"0:MAILING LIST,L,"+CHR$(100)
  180.  
  181. This sequence OPENs the error channel using file #1 and opens the RELative
  182. file called "MAILING LIST" using file #2.  Note the syntax.  The '0:'
  183. specifies drive 0.  You must ALWAYS specify the drive number even on a
  184. single drive.  The ',L,' tells the DOS that this is a RELative file and the
  185. '+CHR$(100)' tells the DOS that the record length of the file is 100 bytes.
  186. Now I think you can see why we constructed those tables earlier.  You needed
  187. that information to tell the drive the size of the file records.
  188.  
  189. This OPENs the file but doesn't really do anything else.  We still need to
  190. create 500 empty records for later use.  To do that we must POSITION the
  191. drive to the last or highest record in the file and write something to that
  192. record.  This action will cause the DOS to create all 500 records and
  193. allocate space on the disk for the whole file.  While it is not absolutely
  194. necessary that you do this, the drive will handle the file much faster if
  195. all of the records are created in advance.  Otherwise, if you position to a
  196. record that isn't present the drive has to create it and any records in
  197. between, a time-consuming process.
  198.  
  199. The position command is passed to the drive through the error channel.  This
  200. is why the error channel must be opened while using a RELative file.  Here
  201. is the syntax for the position command (assuming that file #1 is the error
  202. channel and file #2 is the RELative file):
  203.  
  204.     PRINT#1,"P"CHR$(98)CHR$(RL)CHR$(RH)CHR$(P)
  205.  
  206. Let's take a closer look at this sequence.  PRINT#1 sends the following
  207. commands through the error channel to the DOS.  The "P" tells the DOS that
  208. this is a RELative file position command.  CHR$(98) is the RELative file
  209. number (#2 in this case) plus 96.  CHR$(RL) and CHR$(RH) is the record
  210. number in low byte/high byte format.  It is figured as follows (the variable
  211. J is assumed to be the record number):
  212.  
  213.     RH=INT(J/256):RL=J-RH*256
  214.  
  215. Since we are creating a new file with 500 records the variable J will be set
  216. to equal 500 (J=500).  Using the little equation above a value of 500 for J
  217. will result in RL=244 and RH=1.  Try it!
  218.  
  219. CHR$(P) is the field position within the selected record.  Note that in our
  220. table we constructed earlier, the variable P is used as the position number
  221. for a given field.  Does this make a little more sense now?  For the
  222. purposes of creating the new file P should be set equal to 1 (P=1).  With
  223. the appropriate values set we can then send the position command to the
  224. drive.
  225.  
  226. Arrgghh!!!  What's this???  The drive error light just started flashing.
  227. Now what???
  228.  
  229. Immediately after sending the position command you must read the error
  230. channel for any possible error messages that the drive may send back:
  231.  
  232.     INPUT#1,X,X$,Y,Z
  233.  
  234. The variable X will contain the error number if any, X$ will contain a plain
  235. English error message, and Y and Z will contain the track and sector number
  236. where the error occured (if applicable).  In our example, the drive will
  237. return 50, RECORD NOT PRESENT.  Why, you say?  Well, because record 500
  238. ISN'T PRESENT!  It's a new file, remember?  Well, what do we do?
  239.  
  240. To clear the error condition we must write something to record 500.  This
  241. will force the DOS to create records 1 through 499 as well as 500.  To write
  242. to the 500th record use this syntax:
  243.  
  244.     PRINT#2,"END OF FILE"
  245.  
  246. The drive will run for several minutes and you'll hear all sorts of wild
  247. activity going on inside.  That's the drive head whipping back and forth as
  248. it creates the side sectors and continuously updates the BAM (block
  249. allocation map).  When all of that activity is done your new file will be
  250. ready to go!  Incidentally, you don't have to use "END OF FILE" as the
  251. string to write to the last record.  Anything will do.
  252.  
  253. Following the PRINT#2 sequence you should read the error channel once more
  254. to verify that the drive isn't in trouble again.  If a problem does occur it
  255. will most likely be error 52, FILE TOO LARGE.  This will occur if
  256. insufficient disk space is available to accomodate the file.  The reason for
  257. calculating the estimated file size becomes a bit more obvious now, doesn't
  258. it?
  259.  
  260. Finally you must close up your files, RELative file first followed by the
  261. error channel:
  262.  
  263.     CLOSE2:CLOSE1
  264.  
  265. The above sequence of operations that we have just performed only has to be
  266. used once.  After the file has been created we can simply OPEN and read
  267. and/or write to it without having to specify record length.  It is still
  268. necessary to position the drive however.  We will give that aspect of using
  269. the file further consideration when we start to actually read and write
  270. data.
  271.  
  272. We now have a RELative file on our disk with 500 empty records ready to
  273. accept data.  Before we can start using these records let's set up a few
  274. useful subroutines and variables for our program to work with.
  275. IV.  SUBROUTINES
  276.  
  277. There are certain operations that will be repeatedly used within your
  278. program.  For that reason it is wise to structure them as subroutines to
  279. facilitate the execution of your program.  These operations are: positioning
  280. the drive, reading the error channel, OPENing files and CLOSEing files.
  281. Also, the read and write operations are usually more convenient if
  282. structured as subroutines.
  283.  
  284. It is also convenient to use certain variable names to represent various
  285. pieces of information that will be floating around in your program.  Here
  286. are the variables and their functions that I use:
  287.  
  288.     VAR.  FUNCTION
  289.     =======================================
  290.     I     RECORD INDEX LOOP VARIABLE
  291.     J     RECORD NUMBER
  292.     P     FIELD POSITION IN RECORD
  293.     X     DOS ERROR NUMBER
  294.     X$    DOS ERROR MESSAGE
  295.     EF    ERROR FLAG...1=ERROR  0=NO ERROR
  296.     F     MULTI-PURPOSE LOOP VARIABLE
  297.     Q     CONSTANT EQUAL TO 256
  298.     =======================================
  299.  
  300. As we further progress into more advanced file handling we will see where
  301. all of these variables will become useful.  You of course can use any
  302. variable names that you like.
  303.  
  304. In my RELative file programs I generally use certain line numbers in
  305. connection with the various subroutines required to manipulate the file.
  306. Here is a list of these line numbers:
  307.  
  308.     LINE  FUNCTION
  309.     =======================================
  310.     6900  OPEN ERROR CHANNEL
  311.     7000  OPEN EXISTING RELATIVE FILE
  312.     7100  CLOSE ALL FILES
  313.     8100  POSITION DRIVE TO RECORD & FIELD
  314.     8200  READ ERROR CHANNEL
  315.     8300  WRITE TO RECORD
  316.     8400  READ FROM RECORD
  317.     =======================================
  318.  
  319. You may find that other line numbers may be more convenient.  In all of the
  320. discussions that follow from this point on the above variables and line
  321. numbers will be refered to.
  322.  
  323. Here are the recommended syntaxes for these lines except lines 8300 through
  324. 8499 (we will continue to use the file numbers that we've used up til now):
  325.  
  326.     6900 OPEN1,8,15:RETURN
  327.  
  328.     7000 OPEN2,8,2,"0:MAILING LIST":GOSUB8200:RETURN
  329.  
  330.     8100 CLOSE2:CLOSE1:RETURN
  331.  
  332.     8100 RH=INT(J/Q):RL=J-RH*Q
  333.     8110 PRINT#1,"P"CHR$(98)CHR$(RL)CHR$(RH)CHR$(P)
  334.  
  335.     8200 EF=0:INPUT#1,X,X$:IFX>19THENEF=1
  336.     8210 RETURN
  337.  
  338. Observe that the routine for positioning the drive "falls through" to the
  339. routine that reads the error channel.  This is simply a matter of
  340. programming convenience.  Also notice the syntax in line 7000.  Once the
  341. RELative file has been created your program does not need to specify the
  342. record length.  The DOS will automatically figure it out by itself.  You
  343. must always check the error channel after OPENing the file however, just in
  344. case it was accidentally SCRATCHed from the disk.
  345.  
  346. In line 8100 we first calculate the record low byte/high byte values and
  347. then use them as variables to position the drive.  The constant Q represents
  348. the value 256, a value that is frequently used in RELative file programs.  A
  349. BASIC statement will always evaluate faster using a variable than it will
  350. using the ASCII representation of the number.
  351.  
  352. In 8200 we first clear the error flag EF and then read the error channel.
  353. If the value of X is greater than 19 an error of some type has occured and
  354. the error flag EF is set to one.  It is easier and faster to test EF with an
  355. expression such as IF EF THEN.... than to evaluate X every time an error
  356. check is needed.
  357.  
  358. Well, digest this information and do a little experimenting.  In RELATIVE
  359. FILES MADE RELATIVELY EASY part 2 we'll learn how to actually read and write
  360. the records and how do do field searches.
  361.  
  362. W.J. Brier
  363. Feb. 1986
  364.  
  365.  
  366.