Home   Archive   Permalink



Don't understand objects

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
>>
    


posted by:   Steven White       28-Dec-2015/17:18:51-8:00



Try something like this.....The code creates an object, so you can do stuff like:
    
probe file1/record
make object! [
     NAME: "STEVEN             "
     DOB: "01-MAR-1951"
]
    
The magic is in the line:
    
record: make record compose [(to-set-word fieldname) copy/part RECORD-AREA POSITION/y]
    
    
Full code:
    
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: none        ;; Current record
     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
         record: make object! []    
         foreach [FIELDNAME POSITION] FIELDS [
             RECORD-AREA: head RECORD-AREA
             RECORD-AREA: skip RECORD-AREA (POSITION/x - 1)
             record: make record compose [(to-set-word fieldname) copy/part RECORD-AREA POSITION/y]
         ]
     ]
]
    


posted by:   Sunanda       28-Dec-2015/21:00:18-8:00