Writing files, filtering and reformatting
A huge share of batch programs follow one shape: read a file, pick out the records you want, rearrange them, and write a new file for the next job step. Once you can read a file, writing one is a small step.
Two files at once
Each file gets its own SELECT and FD. You can open several files in one OPEN statement, and close them in one CLOSE:
IDENTIFICATION DIVISION.
PROGRAM-ID. COPYDEMO.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CUST-IN ASSIGN TO CUSTIN
ORGANIZATION IS LINE SEQUENTIAL.
SELECT CUST-OUT ASSIGN TO CUSTOUT
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD CUST-IN.
01 IN-REC.
05 IN-ID PIC X(6).
05 IN-NAME PIC X(20).
05 IN-COUNTRY PIC X(2).
FD CUST-OUT.
01 OUT-REC PIC X(40).
WORKING-STORAGE SECTION.
01 WS-EOF PIC X VALUE "N".
88 END-OF-FILE VALUE "Y".
01 WS-LINE.
05 WS-NAME PIC X(20).
05 FILLER PIC X(3) VALUE " / ".
05 WS-ID PIC X(6).
PROCEDURE DIVISION.
0000-MAIN.
OPEN INPUT CUST-IN
OUTPUT CUST-OUT
PERFORM 8000-READ
PERFORM UNTIL END-OF-FILE
IF IN-COUNTRY = "IE"
MOVE IN-NAME TO WS-NAME
MOVE IN-ID TO WS-ID
WRITE OUT-REC FROM WS-LINE
END-IF
PERFORM 8000-READ
END-PERFORM
CLOSE CUST-IN CUST-OUT
STOP RUN.
8000-READ.
READ CUST-IN
AT END SET END-OF-FILE TO TRUE
END-READ.
Given a CUSTIN dataset of
C00042GRACE HOPPER US
C00107MAEVE BINCHY IE
C00200JOHN BOOLE IE
it writes to CUSTOUT:
MAEVE BINCHY / C00107
JOHN BOOLE / C00200
WRITE and WRITE ... FROM
WRITE record-name writes the FD's record area. WRITE OUT-REC FROM
WS-LINE first moves WS-LINE into OUT-REC, then writes it. Building
the output in WORKING-STORAGE has a practical advantage: FILLER items
there can carry VALUE clauses (like the " / " separator above), which
are not allowed on records in the FILE SECTION (only 88-level condition
names may have VALUE there).
If you build the record directly in the FD record area instead, start
with MOVE SPACES TO the record. Otherwise its FILLER bytes contain
whatever was there before.
Signed numbers in text files
A text dataset needs a visible sign. The clause SIGN IS LEADING
SEPARATE puts it in its own byte in front of the digits:
05 ACCT-BALANCE PIC S9(7)V99
SIGN IS LEADING SEPARATE.
The field is then 10 bytes: -000004550 is minus 45.50, and
+000123456 is 1,234.56. There is no decimal point in the data; V only
marks where it is, as always.
Filtering and reformatting
- Filtering: only WRITE when a condition holds. Count what you read and what you wrote, and display both at the end.
- Reformatting: the output layout can differ completely from the
input: fields reordered, dropped, edited for humans (
-(7)9.99), or padded with separators.
OPEN OUTPUT always creates a new, empty file, even if nothing is
written. An empty output file is a valid result ("no overdrawn accounts
today"), and later job steps rely on it existing.
On the job
Extract programs like these are the glue between systems: a nightly job pulls yesterday's overdrawn accounts for the collections team, new customers for the marketing system, closed accounts for archiving. The reformatting is usually specified in an interface spec: a table of fields, positions and pictures, just like the one in this exercise.
Your task
The collections team wants a daily file of overdrawn accounts.
Input, DD name ACCTIN (line sequential), the account file:
| Field | Picture | Positions |
|---|---|---|
ACCT-NO |
X(8) |
1–8 |
ACCT-NAME |
X(20) |
9–28 |
ACCT-BRANCH |
X(4) |
29–32 |
ACCT-BALANCE |
S9(7)V99 SIGN IS LEADING SEPARATE |
33–42 |
Output, DD name ODOUT (line sequential): one record for each
account with a balance below zero, in this layout (already declared
in the starter as OD-REC):
| Field | Picture |
|---|---|
OD-BRANCH |
X(4) |
FILLER |
X (space) |
OD-ACCT-NO |
X(8) |
FILLER |
X (space) |
OD-NAME |
X(20) |
FILLER |
X (space) |
OD-BALANCE |
-(7)9.99 |
For example:
COR1 10000002 ALAN TURING -45.50
At the end, display the control totals:
READ: 00005
WRITTEN: 00002