I thought I had a clever idea for reading a fixed-format text file, or more specifically, TWO such files at the same time, so I could compare them. I thought I would make an object! that would define a fixed-format file and some functions on it. The functions would allow me to specify field names for the 'fields' on each line, with positions and lengths. Then I could use those specified names as words and refer to the fields by name. Two sample files look like this:
FILE1:
STEVEN 01-MAR-1951
MR. SMITH 02-APR-1952
FILE2:
WILLIAM 01-FEB-1953
MR. JOHNSON 02-MAY-1954
The name starts in position 1 for length 20; the DOB in position 21 for length 11.
In the script below, I define a Fixed-Format File (FFF) object, and then define two objects based on it, FILE1 and FILE2. When I define FILE1 or FILE2, I want to specify that positions 1-20 will be referred to as NAME, and 21-32 by DOB. I pass in the words NAME and DOB, and when I 'read' a 'record,' I 'set' NAME and DOB to substrings extracted out of the data based on the specified positions and lengths.
The above scheme all works fine when I am dealing with only one instance of FFF, but if I try to make TWO, only the NAME and DOB words from the most-recently defined instance are available. As indicated in the comments, I can't refer to FILE1/NAME or FILE2/NAME, but only NAME, which gets me the value from the instance most recently created.
What am I not understanding?
The script is below, followed by the results of running it. Sorry for the length. It really is only two operations. The 'open' function reads the file and defines the 'fields' expected and where they are (by position and length). The 'read' function gets the next line of text and sets the 'field names' to values pulled out of the text line.
Thank you.
R E B O L [
Title: 'Fixed-Format File object'
]
FFF: make object! [
FILE-ID: none ;; file name passed to 'open' function
FIELDS: none ;; [fieldname locationpair fieldname locationpair, etc]
FILE-DATA: [] ;; whole file in memory, as block of lines
RECORD-AREA: '' ;; one line from FILE-DATA, for picking apart
RECORD-NUMBER: 0 ;; for keeping track of which line we picked
FILE-SIZE: 0 ;; number of lines in FILE-DATA
EOF: false ;; set when we 'pick' past end
;; --------------------------
OPEN-INPUT: func [
FILEID [file!] ;; will be a file name
FIELDLIST [block!] ;; will be sets of word! and pair!
] [
;; -- Save what was passed to us.
FILE-ID: FILEID
FIELDS: copy []
FIELDS: copy FIELDLIST
;; -- Read the entire file into memory and set various items in preparation
;; -- for reading the file a record at a time.
FILE-DATA: copy []
FILE-DATA: read/lines FILE-ID
FILE-SIZE: length? FILE-DATA
RECORD-NUMBER: 0
EOF: false
]
;; --------------------------
READ-RECORD: does [
;; pick a line if there are lines left to be picked
RECORD-NUMBER: RECORD-NUMBER + 1
if (RECORD-NUMBER > FILE-SIZE) [
EOF: true
return EOF
]
RECORD-AREA: copy ''
RECORD-AREA: copy pick FILE-DATA RECORD-NUMBER
;; set the words passed to the 'open' function to values extracted
;; out of the data, based on the locations passed to the 'open' function
foreach [FIELDNAME POSITION] FIELDS [
RECORD-AREA: head RECORD-AREA
RECORD-AREA: skip RECORD-AREA (POSITION/x - 1)
set FIELDNAME copy/part RECORD-AREA POSITION/y
]
]
]
;; Now test the object by making two instances of it.
FILE1: make FFF []
FILE1/OPEN-INPUT %rtest1.txt [NAME 1X20 DOB 21X11]
FILE1/READ-RECORD
;; can't say FILE1/NAME or FILE1/DOB; must use just NAME and DOB
print [NAME ' ' DOB]
FILE1/READ-RECORD
print [NAME ' ' DOB]
FILE1/READ-RECORD
probe FILE1/EOF
print '-------------------------'
FILE2: make FFF []
FILE2/OPEN-INPUT %rtest2.txt [NAME 1X20 DOB 21X11]
FILE2/READ-RECORD
;; can't say FILE1/NAME or FILE1/DOB; must use just NAME and DOB
print [NAME ' ' DOB]
FILE2/READ-RECORD
print [NAME ' ' DOB]
FILE2/READ-RECORD
probe FILE2/EOF
print '-------------------------'
;; If we try to refer to a NAME or DOB in FILE1, we crash:
print FILE1/NAME
halt
Results:
STEVEN 01-MAR-1951
MR. SMITH 02-APR-1952
true
-------------------------
WILLIAM 01-FEB-1953
MR. JOHNSON 02-MAY-1954
true
-------------------------
** Script Error: Invalid path value: NAME
** Near: print FILE1/NAME
halt
>>