Reading a file with the priming read
Reading a sequential file means: open it, read records one at a time until there are none left, close it. The only tricky part is knowing there are none left.
AT END
READ file-name fetches the next record into the FD's record area. When
there is no next record, the READ fails with an end-of-file condition,
and the AT END phrase runs instead. The record area is then undefined:
never process it after AT END.
The standard way to use this is an end-of-file flag with an 88-level condition name, and a priming read:
IDENTIFICATION DIVISION.
PROGRAM-ID. READDEMO.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CUST-FILE ASSIGN TO CUSTIN
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD CUST-FILE.
01 CUST-REC.
05 CUST-ID PIC X(6).
05 CUST-NAME PIC X(20).
WORKING-STORAGE SECTION.
01 WS-EOF PIC X VALUE "N".
88 END-OF-FILE VALUE "Y".
01 WS-COUNT PIC 9(3) VALUE 0.
PROCEDURE DIVISION.
0000-MAIN.
OPEN INPUT CUST-FILE
PERFORM 8000-READ-CUST
PERFORM UNTIL END-OF-FILE
ADD 1 TO WS-COUNT
DISPLAY WS-COUNT ": " CUST-NAME
PERFORM 8000-READ-CUST
END-PERFORM
CLOSE CUST-FILE
DISPLAY "RECORDS READ: " WS-COUNT
STOP RUN.
8000-READ-CUST.
READ CUST-FILE
AT END SET END-OF-FILE TO TRUE
END-READ.
With DD_CUSTIN pointing at a dataset holding
C00042GRACE HOPPER
C00107ALAN TURING
the output is
001: GRACE HOPPER
002: ALAN TURING
RECORDS READ: 002
Why read before the loop?
Look at the order: read once before the loop, then at the end of
each pass process the record and read the next one. The loop test
UNTIL END-OF-FILE then always looks at the result of the most recent
READ, before anything touches the record.
- An empty file works: the priming read hits end-of-file straight away, the loop body never runs, and the count is zero.
- The last record is processed exactly once. The read after it sets the flag, and the loop stops before the (now meaningless) record area can be processed again.
Beginners often write the loop the other way round, with the READ at the top of the body. Then the pass that hits end-of-file still falls through to the processing code, and the last record gets counted twice.
One READ paragraph
Put the READ in its own paragraph (8000-READ-CUST) and PERFORM it
from both places. There is then one place to add record counts, status
checks or debugging, and the two reads can never drift apart.
The alternative: NOT AT END
You will also meet this style in existing code:
PERFORM UNTIL END-OF-FILE
READ CUST-FILE
AT END SET END-OF-FILE TO TRUE
NOT AT END PERFORM 1000-PROCESS
END-READ
END-PERFORM
It is correct too: the processing only happens on a successful read. Many shop standards prefer the priming read because the main loop reads as plain steps ("process this record, get the next"), and it scales better when you are reading two files in step, as you will in the match-merge lesson.
On the job
Batch programs are expected to report their control totals, such as records read and records written, at the end of the run. Operators and auditors compare them between job steps: if step 1 wrote 10,482 records and step 2 read 10,481, something is wrong. Count every read.
Your task
Write a payroll listing. The employee file is assigned to DD name
EMPIN (line sequential). Each record is:
| Field | Picture | Positions |
|---|---|---|
EMP-ID |
X(5) |
1–5 |
EMP-NAME |
X(20) |
6–25 |
EMP-DEPT |
X(3) |
26–28 |
EMP-SALARY |
9(6)V99 |
29–36 |
For each employee display the ID, name and salary (via WS-SALARY-ED),
separated by single spaces. After the last record display the number of
employees and the total salary:
E0001 GRACE HOPPER 85000.00
E0002 ALAN TURING 72500.50
E0003 ADA LOVELACE 93000.00
EMPLOYEES: 003
PAYROLL: 250500.50
Use the priming-read pattern. The file may be empty.