Reference modification
Often you need part of a field: the year of a date, the last four digits of a card number, the first letter of a name. Reference modification lets you address any substring of a data item without declaring a subfield for it:
identifier(start:length)
start is the 1-based position of the first byte, length is how many
bytes. Leave out the length to mean "to the end of the field".
IDENTIFICATION DIVISION.
PROGRAM-ID. REFMOD.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DATE PIC X(8) VALUE "20260924".
01 WS-NAME PIC X(12) VALUE "HOPPER".
PROCEDURE DIVISION.
DISPLAY WS-DATE(1:4) "-" WS-DATE(5:2) "-" WS-DATE(7:)
DISPLAY "INITIAL: " WS-NAME(1:1)
STOP RUN.
Output:
2026-09-24
INITIAL: H
Variables as positions
Both start and length can be numeric data items or arithmetic
expressions, which makes reference modification the way to walk through a
field one character at a time:
IDENTIFICATION DIVISION.
PROGRAM-ID. SPELL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-WORD PIC X(5) VALUE "COBOL".
01 WS-I PIC 9(2).
PROCEDURE DIVISION.
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 5
DISPLAY WS-I ": " WS-WORD(WS-I:1)
END-PERFORM
STOP RUN.
This prints 01: C, 02: O, and so on. WS-WORD(WS-I + 1:2) works too.
As a receiving field
A reference-modified item can be the target of a MOVE. Only the addressed bytes change; the rest of the field is untouched:
IDENTIFICATION DIVISION.
PROGRAM-ID. MASK.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ACCT PIC X(10) VALUE "4400123987".
PROCEDURE DIVISION.
MOVE ALL "*" TO WS-ACCT(1:6)
DISPLAY WS-ACCT
STOP RUN.
Output: ******3987. MOVE ALL "*" fills the whole receiving area (here
six bytes) with asterisks.
Things to watch
- The result of reference modification is always alphanumeric, even if
the field is numeric.
WS-AMOUNT(1:3)gives you three digit characters, not a number. - Positions are not checked by default.
WS-DATE(7:5)on an 8-byte field reads past the end into whatever is next in storage. On the mainframe, compile withSSRANGEduring testing to catch this. - Positions count bytes. On the mainframe, text is EBCDIC, one byte per character, so this is rarely a surprise.
On the job
Old programs often define a date field with a REDEFINES and three
subfields just to get at the year. Reference modification does the
same job inline, and you will see both styles side by side in legacy
code. Neither is wrong; follow the style of the program you are
changing.
Your task
A card statement program needs two formatted values. Read two lines from input:
- A transaction date as
YYYYMMDD, e.g.20260924 - A 16-digit card number, e.g.
4929123456783456
Display the date in day/month/year order and the card number with all but the last four digits masked:
DATE: 24/09/2026
CARD: ************3456
Use reference modification to pick the pieces out and to mask the card number. Do not declare subfields or use REDEFINES.