Intermediate

I/O Operations in COBOL

Master input and output in COBOL - console DISPLAY/ACCEPT, formatted output with PIC clauses, and sequential file reading and writing with GnuCOBOL

Input and output are where COBOL shows its true colors. The language was born in 1959 to process business records - reading files, transforming data, and writing reports - so its I/O model is one of the most mature and precisely controlled of any language. Where modern languages treat file handling as an afterthought, COBOL bakes it into the program structure through the ENVIRONMENT and DATA divisions.

COBOL separates I/O into two worlds. Console I/O uses the simple DISPLAY and ACCEPT verbs to talk to the terminal. File I/O is far more structured: you declare a file in the FILE-CONTROL paragraph, describe its record layout with an FD (File Description) entry, and then OPEN, READ/WRITE, and CLOSE it. Every byte of every record has a defined position and picture.

This precision is why COBOL still runs banking and government systems: a PIC 9(6) field is always six digits, and a record layout written in 1975 reads exactly the same way today. In this tutorial you’ll learn formatted console output, interactive input, and how to write and read sequential files - the everyday tools of COBOL data processing.

Formatted Console Output

You already met DISPLAY in Hello World. Its real power emerges with numeric-edited pictures, which turn raw numbers into human-readable output. DISPLAY ... WITH NO ADVANCING suppresses the newline so you can build a line piece by piece.

Create a file named display.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
IDENTIFICATION DIVISION.
PROGRAM-ID. DISPLAY-DEMO.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNT      PIC 9(3)    VALUE 42.
01 WS-PRICE      PIC 9(4)V99 VALUE 1234.56.
01 WS-CURRENCY   PIC Z,ZZ9.99.
01 WS-NAME       PIC X(15)   VALUE "COBOL".

PROCEDURE DIVISION.
    DISPLAY "Item count: " WS-COUNT
    MOVE WS-PRICE TO WS-CURRENCY
    DISPLAY "Price: $" WS-CURRENCY
    DISPLAY "Language: " FUNCTION TRIM(WS-NAME)
    DISPLAY "Progress: " WITH NO ADVANCING
    DISPLAY "50%" WITH NO ADVANCING
    DISPLAY " complete"
    STOP RUN.

A few things are happening here:

  • WS-COUNT is stored as 042 - numeric fields carry leading zeros, and DISPLAY shows them literally.
  • WS-CURRENCY uses an edited picture. The Z characters suppress leading zeros (turning them into spaces), while , and . are inserted literally. Moving 1234.56 into Z,ZZ9.99 produces 1,234.56.
  • FUNCTION TRIM strips the trailing spaces that pad the fixed-width PIC X(15) field.
  • WITH NO ADVANCING keeps the cursor on the same line so three DISPLAY statements build one line of output.

Interactive Input with ACCEPT

The ACCEPT verb reads a line from standard input into a data item. COBOL moves the typed characters into the receiving field according to its picture, so text goes into PIC X fields and digits into PIC 9 fields. ACCEPT ... FROM DATE (and FROM TIME) pulls values from the system instead of the keyboard.

Create a file named accept.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
IDENTIFICATION DIVISION.
PROGRAM-ID. ACCEPT-DEMO.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NAME    PIC X(30).
01 WS-CITY    PIC X(30).
01 WS-TODAY   PIC 9(6).

PROCEDURE DIVISION.
    DISPLAY "Enter your name: " WITH NO ADVANCING
    ACCEPT WS-NAME
    DISPLAY "Enter your city: " WITH NO ADVANCING
    ACCEPT WS-CITY
    ACCEPT WS-TODAY FROM DATE
    DISPLAY " "
    DISPLAY "Welcome, " FUNCTION TRIM(WS-NAME) "!"
    DISPLAY "You are visiting from " FUNCTION TRIM(WS-CITY) "."
    DISPLAY "Today's date (YYMMDD): " WS-TODAY
    STOP RUN.

Because ACCEPT reads interactively, output depends on what the user types. A sample session (with the user entering Grace Hopper and Philadelphia) looks like this:

Enter your name: Grace Hopper
Enter your city: Philadelphia

Welcome, Grace Hopper!
You are visiting from Philadelphia.
Today's date (YYMMDD): 260702

The ACCEPT WS-TODAY FROM DATE line reads the current date as YYMMDD (here, July 2, 2026 → 260702) - a handy way to timestamp reports without any external library.

Writing a Sequential File

File I/O is where COBOL’s structure pays off. To use a file you must declare it in three places: FILE-CONTROL (name and organization), an FD entry (the record layout), and the PROCEDURE DIVISION (the OPEN/WRITE/CLOSE logic). We use ORGANIZATION IS LINE SEQUENTIAL, which writes plain text lines - the most portable file format.

Create a file named filewrit.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
IDENTIFICATION DIVISION.
PROGRAM-ID. FILE-WRITE.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT REPORT-FILE ASSIGN TO "sales.dat"
        ORGANIZATION IS LINE SEQUENTIAL.

DATA DIVISION.
FILE SECTION.
FD REPORT-FILE.
01 SALES-RECORD.
    05 SR-PRODUCT   PIC X(12).
    05 SR-UNITS     PIC 9(4).
    05 SR-REVENUE   PIC 9(6).

WORKING-STORAGE SECTION.
01 WS-DUMMY        PIC X VALUE SPACE.

PROCEDURE DIVISION.
    OPEN OUTPUT REPORT-FILE
    MOVE "Keyboard" TO SR-PRODUCT
    MOVE 150 TO SR-UNITS
    MOVE 4500 TO SR-REVENUE
    WRITE SALES-RECORD
    MOVE "Monitor" TO SR-PRODUCT
    MOVE 75 TO SR-UNITS
    MOVE 22500 TO SR-REVENUE
    WRITE SALES-RECORD
    MOVE "Mouse" TO SR-PRODUCT
    MOVE 300 TO SR-UNITS
    MOVE 3600 TO SR-REVENUE
    WRITE SALES-RECORD
    CLOSE REPORT-FILE
    DISPLAY "Wrote 3 records to sales.dat"
    STOP RUN.

Key ideas:

  • SELECT ... ASSIGN TO "sales.dat" maps the internal file name REPORT-FILE to a real file on disk.
  • The FD entry defines a fixed 22-byte record: 12 characters of product name, 4 digits of units, 6 digits of revenue. Every record has this exact layout.
  • OPEN OUTPUT creates (or truncates) the file. You must OPEN before any WRITE and CLOSE when done.
  • Each WRITE SALES-RECORD flushes the current contents of the record area to disk as one line. Because the fields are fixed-width, Keyboard is padded to 12 characters and numbers keep their leading zeros.

Reading a Sequential File

Reading walks through the file record by record. The READ ... AT END construct handles the end-of-file condition, and a PERFORM UNTIL loop keeps reading until there are no more records. This example also adds a FILE STATUS field to catch I/O errors - the standard COBOL way to handle a missing or unreadable file.

Create a file named fileread.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
IDENTIFICATION DIVISION.
PROGRAM-ID. FILE-READ.

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT REPORT-FILE ASSIGN TO "sales.dat"
        ORGANIZATION IS LINE SEQUENTIAL
        FILE STATUS IS WS-FILE-STATUS.

DATA DIVISION.
FILE SECTION.
FD REPORT-FILE.
01 SALES-RECORD.
    05 SR-PRODUCT   PIC X(12).
    05 SR-UNITS     PIC 9(4).
    05 SR-REVENUE   PIC 9(6).

WORKING-STORAGE SECTION.
01 WS-FILE-STATUS  PIC XX   VALUE "00".
01 WS-EOF          PIC X    VALUE "N".
01 WS-COUNT        PIC 9(2) VALUE 0.

PROCEDURE DIVISION.
    OPEN INPUT REPORT-FILE
    IF WS-FILE-STATUS NOT = "00"
        DISPLAY "Error opening file: " WS-FILE-STATUS
        STOP RUN
    END-IF
    PERFORM UNTIL WS-EOF = "Y"
        READ REPORT-FILE
            AT END
                MOVE "Y" TO WS-EOF
            NOT AT END
                ADD 1 TO WS-COUNT
                DISPLAY "Product: " FUNCTION TRIM(SR-PRODUCT)
                    " | Units: " SR-UNITS
                    " | Revenue: " SR-REVENUE
        END-READ
    END-PERFORM
    CLOSE REPORT-FILE
    DISPLAY "Total records read: " WS-COUNT
    STOP RUN.

How the read loop works:

  • FILE STATUS IS WS-FILE-STATUS gives you a two-character code after each I/O operation. "00" means success; anything else signals a problem (for example, "35" for a file that doesn’t exist). Checking it after OPEN is defensive programming COBOL shops rely on.
  • READ REPORT-FILE loads the next record into SALES-RECORD. The AT END branch fires when there are no more records; NOT AT END runs for each successful read.
  • The loop uses the WS-EOF flag as its exit condition - a classic COBOL idiom since the language has no break statement.
  • Reading fills the same fixed-width fields defined in the FD, so SR-UNITS and SR-REVENUE come back as the exact digit strings that were written.

Running with Docker

The examples run in the official GnuCOBOL image. The -x flag builds an executable and -free enables free-format source. The file examples must run in order (write creates sales.dat, then read consumes it).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Pull the official image
docker pull esolang/cobol:latest

# Run the formatted output example
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free display.cob && ./display'

# Run the interactive input example (type values when prompted)
docker run --rm -it -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free accept.cob && ./accept'

# Write a file, then read it back
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free filewrit.cob && ./filewrit && cobc -x -free fileread.cob && ./fileread'

Expected Output

The formatted output example (display.cob) prints:

Item count: 042
Price: $1,234.56
Language: COBOL
Progress: 50% complete

The write-then-read sequence (filewrit.cob followed by fileread.cob) prints:

Wrote 3 records to sales.dat
Product: Keyboard | Units: 0150 | Revenue: 004500
Product: Monitor | Units: 0075 | Revenue: 022500
Product: Mouse | Units: 0300 | Revenue: 003600
Total records read: 03

Key Concepts

  • Console vs. file I/O are separate worlds - DISPLAY/ACCEPT for the terminal, and a full FILE-CONTROL + FD + OPEN/READ/WRITE/CLOSE pipeline for files.
  • Edited pictures format numbers for humans - Z suppresses leading zeros while , . $ are inserted literally, so raw numeric fields become readable reports without string juggling.
  • Every file has a fixed record layout - the FD entry defines exact byte positions with PIC clauses, which is why a COBOL data file stays readable for decades.
  • OPEN before use, CLOSE when done - files must be opened in the right mode (OUTPUT, INPUT, EXTEND, or I-O) before any read or write.
  • READ ... AT END drives the read loop - combined with a PERFORM UNTIL flag, it’s the canonical way to process a sequential file since COBOL has no break.
  • FILE STATUS is your error handling - a two-character code after every operation ("00" = success) lets you catch missing files and I/O failures gracefully.
  • ORGANIZATION IS LINE SEQUENTIAL produces portable plain-text files, ideal for interoperating with modern tools; classic mainframe code often uses record-oriented SEQUENTIAL or indexed files instead.

Running Today

All examples can be run using Docker:

docker pull esolang/cobol:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining