Reference Modification: COBOL's Substring

COBOL COBOL 2014 (ISO/IEC 1989:2014), built with GnuCOBOL 3.x · ✓ verified by execution on 2026-08-16

Most programming languages use functions or methods like substring() to extract a portion of a string. COBOL does it natively using a syntax called reference modification. In modern high-level languages, strings are treated as dynamic objects, and you manipulate them by calling library methods. COBOL, designed for processing massive fixed-width flat files, treats strings and alphanumeric data as strict blocks of memory. Because of this, it provides a powerful, built-in syntax to slice directly into memory without overhead.

The COBOL 1985 standard introduced the concept of a reference modifier to facilitate references to only a portion of a data item. You simply append parentheses containing a starting position and a length directly to the variable name: identifier(start:length).

The 1-Based Index

If you are coming from C, Java, Python, or JavaScript, the most important thing to learn about COBOL reference modification is that COBOL uses 1-based indexing. The start value indicates the starting character position being referenced, and character position values start with 1, not 0.

Here is a visual representation of how a COBOL string sits in memory:

J 1 O 2 H 3 N 4 A 5 T 6 H 7 A 8 N 9

To extract the first 9 characters of a string, you start at position 1 and specify a length of 9:

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. BASIC-SUB.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-FULL-NAME    PIC X(20) VALUE 'JOHNATHAN DOE'.
       01  WS-FIRST-NAME   PIC X(10).
       PROCEDURE DIVISION.
           MOVE WS-FULL-NAME(1:9) TO WS-FIRST-NAME.
           DISPLAY 'First name: ' WS-FIRST-NAME.
           STOP RUN.
Output
First name: JOHNATHAN 

The Length vs End-Index Trap

When writing (start:length), the second number is the length of the substring, not the ending position. This trips up nearly everyone when they first write COBOL.

Predict the output cobol

Read the code. What exactly will it print? Commit to an answer before you look.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. LEN-VS-END.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ALPHABET     PIC X(10) VALUE 'ABCDEFGHIJ'.
       01  WS-PART         PIC X(5).
       PROCEDURE DIVISION.
           MOVE WS-ALPHABET(3:5) TO WS-PART.
           DISPLAY 'Part: ' WS-PART.
           STOP RUN.
Output
Part: CDEFG

Notice that WS-ALPHABET(3:5) does not mean “from position 3 to position 5”. It means “start at position 3, and grab 5 characters”.

Extracting to the End

If you omit the length parameter completely, COBOL is smart enough to assume you want everything from your specified starting position all the way to the very end of the variable’s defined PIC length. This is particularly useful when extracting a variable-length suffix or when the exact length is cumbersome to calculate manually.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. END-STR.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-FULL-NAME    PIC X(20) VALUE 'JOHNATHAN DOE'.
       01  WS-LAST-NAME    PIC X(10).
       PROCEDURE DIVISION.
           MOVE WS-FULL-NAME(11:) TO WS-LAST-NAME.
           DISPLAY 'Last name: ' WS-LAST-NAME.
           STOP RUN.
Output
Last name: DOE       

Modifying Substrings

Reference modification is not just for reading parts of a string. It may be used anywhere an identifier is legal, including serving as the receiving field of statements like MOVE. You can overwrite a specific slice of a variable without touching the rest of it.

When you use reference modification on the receiving side of a MOVE statement, the rest of the variable remains completely untouched. This is incredibly useful for manipulating fixed-format records, such as updating only the month portion of a date field or replacing a specific digit in a serial number.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. WRITE-SUB.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DATE         PIC X(10) VALUE '2026-01-01'.
       PROCEDURE DIVISION.
           MOVE '12' TO WS-DATE(6:2).
           DISPLAY 'New Date: ' WS-DATE.
           STOP RUN.
Output
New Date: 2026-12-01

Dynamic Reference Modification

You do not have to hardcode the start and length values. Both start and length may be specified as integer numeric literals, integer numeric data items, or arithmetic expressions with an integer value.

In real-world mainframe applications, you will often need to parse data where the positions are not known until runtime. COBOL allows you to use variables instead of hardcoded numbers. When doing this, be extremely careful: if your variables cause the start or length to exceed the variable’s boundaries, you may trigger an out-of-bounds error or silently corrupt adjacent memory, depending on the compiler.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. VAR-IDX.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-TEXT         PIC X(15) VALUE 'APPLE BANANA'.
       01  WS-START        PIC 99 VALUE 7.
       01  WS-LEN          PIC 99 VALUE 6.
       01  WS-RESULT       PIC X(10).
       PROCEDURE DIVISION.
           MOVE WS-TEXT(WS-START:WS-LEN) TO WS-RESULT.
           DISPLAY 'Fruit: ' WS-RESULT.
           STOP RUN.
Output
Fruit: BANANA    

You can even combine reference modification on both the source and destination fields in the same statement:

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. SUB-TO-SUB.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SOURCE       PIC X(15) VALUE 'ID: 987654321'.
       01  WS-DEST         PIC X(15) VALUE 'USER-000000'.
       PROCEDURE DIVISION.
           MOVE WS-SOURCE(5:6) TO WS-DEST(6:6).
           DISPLAY 'Dest: ' WS-DEST.
           STOP RUN.
Output
Dest: USER-987654    

Check yourself

In COBOL reference modification, what does `WS-DATA(3:5)` mean?

Reveal answer

Extract 5 characters, starting at position 3 — The first number is the starting position (1-based). The second number is the LENGTH, not the end position.

What happens if you omit the length, as in `WS-DATA(4:)`?

Reveal answer

It extracts everything from position 4 to the end of the string — If no length is specified, COBOL assumes a value equivalent to the remaining character positions from the start to the end of the item.

Are COBOL strings 0-indexed or 1-indexed?

Reveal answer

1-indexed (the first character is position 1) — COBOL uses 1-based indexing. Position 1 is the leftmost character.

Challenges

Challenge 1 +10 XP

Extract the year from WS-DATE into WS-YEAR using reference modification.

cobol ✓ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real — it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 — expects "Year is: 2026\n"
Need a hint? (−25% XP)

Start at character 7 for a length of 4.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. EXTRACT-YEAR.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  WS-DATE         PIC X(10) VALUE '15/08/2026'.
01  WS-YEAR         PIC X(4).
PROCEDURE DIVISION.
    MOVE WS-DATE(7:4) TO WS-YEAR.
    DISPLAY 'Year is: ' WS-YEAR.
    STOP RUN.

🐞 Bug Hunt +15 XP

This program has a silent bug. It tries to extract the 5-character serial from the part number starting after the PX-, but it gets it wrong. Find and fix the error.

This code runs. It just does the wrong thing. Read it, find the defect, fix it — the tests below decide when you are right.

cobol ✓ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real — it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 — expects "Serial: 10294\n"
Need a hint? (−25% XP)

The first character is at position 1. The P is 1, X is 2, the hyphen is 3, so the serial starts at 4.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. BUGHUNT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  WS-PART-NO      PIC X(10) VALUE 'PX-10294-A'.
01  WS-SERIAL       PIC X(5).
PROCEDURE DIVISION.
    MOVE WS-PART-NO(4:5) TO WS-SERIAL.
    DISPLAY 'Serial: ' WS-SERIAL.
    STOP RUN.