Reputation: 157
For an assignment as part of a COBOL course, my input file is a sequential file called BRANCHTOT.SEQ with two types of records:
The first is what I call a header record that states the total amount of records in the file (excluding the header record itself). This is only one record.
If this were to be the only record in the file, I'd define it as follows:
01 header-record.
03 record-count PIC 9(6).
03 FILLER PIC X(13).
The second type of records are the records I will have to read and process for the output. Its structure is as follows:
01 sales-record.
03 branch-nr PIC X(5).
03 office-nr PIC 9(2).
03 count PIC 9(5).
03 sum PIC 9(5)V99.
I'm used to working with files with one type of record.
The assignment is much more complex than I'm describing here, but for this part of the problem my program has to read and process the sales-record records and print the total amount of these records it has read, and compare that to the amount listed in the header-record. If the two don't match I need to have a statement in my output list to note this error.
Upvotes: 2
Views: 3596
Reputation: 13076
You define both of your records, you can do so exactly as you have shown, under a single FD
. This will give you an implicit REDEFINES.
Use FILE STATUS (always) on your SELECT statement for the file. Always check that the file-status field gets an expected value. Use an 88 condition-name for a value of "10" to identify end-of-file. Do not use AT END
/NOT AT END
.
Note that you can't do this perfectly for your exercise, because the file has not been defined correctly. There is no indication available to identify either a header record, or a data record. The only indication is that "the header is first, all the rest are data". That may seem fine, but turns to slok when someone pickles the program writing the file originally, and gives you no header, or two-or-more headers.
If a file has a structure, the structure should be within the data, because it can then be checked. Processing a bad file as though it were a good one can be very costly. And embarrassing.
Also you need to know whether it is permissible for the file to be "empty". Since it has a header record, an "empty" file should consist of a header record with a zero data-record count. Your case may be different, as the file is not designed.
Within your initial processing (after your input file is OPEN
ed), you read the first record. Deal with the "empty file" if it is so. Check it is a header. Store the record-count.
You then read the next record. You check it is not a header.
Then you process your data, remembering that you have the first data-record available.
Loop until end-of-input
process record
read next record
End-Loop
At end-of-file (when the loop finishes) you check the number of records on the header (from the stored value) to the number of data-records you read (counted within the processing of the record).
Once you have your program, you then have a "model" to base other programs on. You only have to get the general "this is the way I process a file" correct once, and then use that as a starting-point for your next file-processing program. That next program will be more complicated, so you end up with another model.
After time you'll have about five models, each based on the working code from a simpler task.
There are several reasons I argue against the use of AT END
/NOT AT END
for READ of a file:
Complexity and understandability
a-pargraph-for-SO-formatting.
PERFORM priming-read
PERFORM
UNTIL end-of-infile
PERFORM process-data
PEFORM read-next
END-PERFORM
.
priming-read.
PERFORM read-next
.
read-next.
READ IN-FILE
IF NOT IN-FILE-STATUS-OK
PERFORM diagnostic-message-and-fail
END-IF
.
Vs.
PERFORM UNTIL WS-EOF='Y'
READ STUDENT INTO WS-STUDENT
AT END MOVE 'Y' TO WS-EOF
NOT AT END DISPLAY WS-STUDENT
END-READ
END-PERFORM
You can't put any code after the END-READ without testing the WS-EOF. Yet people do.
Reliability and ease of operation
If FILE STATUS is specified for a file in the SELECT then it is down to the programmer to test it. If something is wrong, AT END, clearly, is not true, yet there is no new record. A subsequent READ will get the same situation, and a Big Fat Loop ensues. So the file-status-field should be tested (if FILE STATUS is used), so, why not use the file-status-field to test for end-of-file, since it is simpler, rather than a further condition within the NOT AT END.
Of course, if you don't use FILE STATUS, the run-time will deal with things, but in a broad-and-blunt manner, with no opportunity for the supply of additional diagnostic information,
Also of course, you could use USE AFTER... but that further complicates, with something many aren't used to.
It encourages the use of GO TO to "get out of a mess"
READ IN-FILE
AT END GO TO no-more-records
END-READ
Why define the records under the FD, why not in WORKING-STORAGE
Or, what is the difference between READ ...
and READ ... INTO ...
?
The FD
in the FILE SECTION
allows description of the record in the "record area".
For an input file, this will be the last record successfully read. If there is one. The OPEN of a file will make a record-area available. Once end-of-file is encountered, there will no longer be a current record. Neither will there be a current record when the file is CLOSEd (whether end-of-file is reached or not).
This means you shouldn't access a record-area before a file is OPENed, after it is CLOSEd, or after end-of-file is reached. On an IBM Mainframe shouldn't is often can't as it can readily cause a S0C4
abend, a Protection Exception. The input area is actually defined in the IO-routines which process the files, not in your COBOL program. The FD just maps your definitions to the address of the record-area. If the record-area doesn't exist at the time, you can't access it.
For a simple file-structure, where you don't need access to data from different records at the same time, you can always use the FD.
For a more complex structure, you need to store data from the different record-types, as only the current record is available under the FD.
You can store the entire record, or just the parts you need.
You can store just the parts you need with MOVEs for the individual fields at some point after the READ.
You can store entire records by a MOVE of the entire record under the FD after the READ, or by using READ ... INTO ... which does that automatically.
READ ... INTO ... does the MOVE (implicit) for every record on your input file. If you don't need that, it is a waste of resources, and since people pay for resources (like CPU used) on a Mainframe, it is worth avoiding unless you desperately need it.
Sites usually have local standards. You follow the standards, even if they are not good (you try to change them, don't always succeed). If you are told to use READ ... INTO ... you use it.
However, for information, I don't use READ ... INTO ... (unless in the above case) and have never encountered a problem using the FD and MOVEing the data I want (either individual fields, or an entire record).
It is "best" to use the FD. Unless the local standard dictates otherwise. Then it is "best" to follow the local standard.
Just to note, there are some things which modify the above and create a specific area for a record in your program. If INTO (and FROM on a WRITE) can get your entire record MOVEd implicitly twice.
Upvotes: 3
Reputation: 326
OK, thanks for your comment, it's for a course, so it's rather free in terms of style. Here is an excellent beginning for reading a file
Typically, if you speak about "redefines", it means your teacher expects you using one. So your record would be defined something like that :
01 my-record.
05 my-header.
10 record-count PIC 9(6).
10 FILLER PIC X(13).
05 my-sales-record redefines my-header.
10 branch-nr PIC X(5).
10 office-nr PIC 9(2).
10 count PIC 9(5).
10 sum PIC 9(5)V99.
It works, because both are 19 characters long. No need for additional fillers, or complicated things.
Then, later, you'll have a
READ myfile INTO my-record
Use a counter(I hope you know how to use a simple numeric variable) to count the records. And use it to know if it's the header :
IF current-record = 1
(do something with the header)
ELSE
(do something with the sales record)
END-IF
Be sure that all this is embedded in a loop like the one described in my example link, and it should work.
Upvotes: 0