Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

COBOL  Module 5 – Text handling: STRING, UNSTRING, INSPECT and text functions

STRING, UNSTRING and reference modification

Join COBOL text with STRING, DELIMITED BY and POINTER, split delimited lines with UNSTRING and COUNT IN, and cut substrings with reference modification safely.

  • Intermediate
  • 20 minutes
  • Examples run with GnuCOBOL 3.2 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Join fields with STRING using DELIMITED BY, POINTER and ON OVERFLOW
  • Split delimited input with UNSTRING, including empty fields, COUNT IN and TALLYING IN
  • Extract and overwrite substrings with reference modification and keep them in bounds
  • Predict what STRING and UNSTRING leave in a receiving field that is too short or not cleared

Before you start

What you’ll learn · Before you start

What you will learn

  • Join fields with STRING using DELIMITED BY, POINTER and ON OVERFLOW
  • Split delimited input with UNSTRING, including empty fields, COUNT IN and TALLYING IN
  • Extract and overwrite substrings with reference modification and keep them in bounds
  • Predict what STRING and UNSTRING leave in a receiving field that is too short or not cleared

Before you start

On this page

A statement printed ANITADESAI for a customer called Anita Desai. The STRING statement that joined her two names had a space between them, but the space was dropped, because one DELIMITED BY phrase covered the space too, and nothing warned about it.

Below you will join fields with STRING and track where it stops with POINTER and ON OVERFLOW, split delimited lines with UNSTRING, including empty fields and lines with a field too many, and read and overwrite parts of a field with reference modification without running past its end.

STRING copies each sending item up to its delimiter

STRING joins sending items, literals or data items, into one receiving item. Each item is copied up to its delimiter: A character or string that marks where a piece of text ends. STRING copies each sending item up to its delimiter, and UNSTRING splits its sending item at each one.: DELIMITED BY SPACE stops at the first space, and IBM’s reference says DELIMITED BY SIZE moves the entire sending item. The trap is in how the phrases attach. A DELIMITED BY phrase covers every sending item before it that has no phrase of its own, literals included. This program joins a first and a last name three times:

names.cob: two names joined three ways COB · names.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. NAMES.
      * STRING copies each sending item up to its delimiter. A phrase
      * DELIMITED BY applies to every item before it that has none,
      * literals included.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  FIRST-NAME   PIC X(10) VALUE "ANITA".
       01  LAST-NAME    PIC X(10) VALUE "DESAI".
       01  FULL-NAME    PIC X(21).
       PROCEDURE DIVISION.
           MOVE SPACES TO FULL-NAME
           STRING FIRST-NAME DELIMITED BY SPACE
                  " "
                  LAST-NAME DELIMITED BY SPACE
               INTO FULL-NAME
           END-STRING
           DISPLAY "space delimited by SPACE: [" FULL-NAME "]"
           MOVE SPACES TO FULL-NAME
           STRING FIRST-NAME DELIMITED BY SPACE
                  " " DELIMITED BY SIZE
                  LAST-NAME DELIMITED BY SPACE
               INTO FULL-NAME
           END-STRING
           DISPLAY "space delimited by SIZE:  [" FULL-NAME "]"
           STRING "RAVI" DELIMITED BY SIZE
               INTO FULL-NAME
           END-STRING
           DISPLAY "RAVI, receiver not cleared: [" FULL-NAME "]"
           STOP RUN.
The delimiter trap, its fix, and a receiver not cleared Python · names_session.py
from session import files, run

# Joining a first and a last name with STRING: the delimiter trap, its fix, and a STRING into a receiver that still
# holds the previous name.
files("names.cob")
run("""
cobc -x names.cob && ./names
""")

Output

$ cobc -x names.cob && ./names
space delimited by SPACE: [ANITADESAI           ]
space delimited by SIZE:  [ANITA DESAI          ]
RAVI, receiver not cleared: [RAVIA DESAI          ]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 names_session.py

The first line is the opening’s ANITADESAI. The literal “ “ had no DELIMITED BY of its own, so DELIMITED BY SPACE, written after LAST-NAME, covered it as well, and a space delimited by a space contributes nothing. Step through the three sending items:

Interactive · stage by stage What each sending item of the STRING gives Step down the column for the first STRING, then see what DELIMITED BY SIZE changes for the space.
What each sending item of the STRING gives, step 5 of 5. Showing FIRST-NAME (gives ANITA), The literal " " (SIZE: gives one space), LAST-NAME (gives DESAI), FULL-NAME (ANITA DESAI). In focus: The literal " ", FULL-NAME.FIRST-NAMEgives ANITAThe literal " "SIZE: gives one spaceLAST-NAMEgives DESAIFULL-NAMEANITA DESAI

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The first name. FIRST-NAME holds ANITA and five spaces. Its delimiter is SPACE, so STRING copies the characters before the first space, ANITA, and moves on.
  2. The space literal. The literal " " has no DELIMITED BY of its own, so the phrase after LAST-NAME, DELIMITED BY SPACE, applies to it too. Its first character is the delimiter, so it gives nothing at all.
  3. The last name. LAST-NAME gives DESAI, the characters before its first space.
  4. The joined name. FULL-NAME receives ANITA and then DESAI with nothing between them, ANITADESAI, as the first line of the session shows. No error or warning says that a piece gave nothing.
  5. The fix. The second STRING writes " " DELIMITED BY SIZE, so the literal is copied whole, one space, and FULL-NAME becomes ANITA DESAI.

The second STRING gives the literal its own DELIMITED BY SIZE, and the result is ANITA DESAI. Only the names, which are padded with spaces in their 10-byte fields, need DELIMITED BY SPACE; a literal is already exactly what should be copied.

A literal without DELIMITED BY SIZE

Wrong: STRING FIRST-NAME DELIMITED BY SPACE “ “ LAST-NAME DELIMITED BY SPACE, which drops the space. Right: STRING FIRST-NAME DELIMITED BY SPACE “ “ DELIMITED BY SIZE LAST-NAME DELIMITED BY SPACE, so every literal states that it is copied whole.

STRING never clears its receiver, and POINTER says where it stopped

The third line of the session shows the other half of STRING’s behaviour. STRING “RAVI” wrote four characters over the previous name and left the rest alone, so the receiver read RAVIA DESAI: STRING writes only the positions it fills. Move SPACES to a receiver before every STRING that builds it from scratch.

POINTER keeps the position where copying continues. It must start at 1 or more before the statement runs, and STRING moves it past every character it copies, so two STRING statements with the same pointer build one line in pieces. When the pointer goes past the end of the receiver, IBM’s reference says that “no more data is transferred”, and ON OVERFLOW runs if it is coded:

pointer.cob: one line built in two pieces, then a short receiver COB · pointer.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. POINTER1.
      * A statement line built in two STRING statements that share
      * one POINTER, then the same name into a receiver of 8 bytes.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  FIRST-NAME   PIC X(10) VALUE "ANITA".
       01  LAST-NAME    PIC X(10) VALUE "DESAI".
       01  ACCT-NO      PIC X(6)  VALUE "AC0417".
       01  STMT-LINE    PIC X(30).
       01  SHORT-NAME   PIC X(8).
       01  PTR          PIC 99.
       PROCEDURE DIVISION.
           MOVE SPACES TO STMT-LINE
           MOVE 1 TO PTR
           STRING ACCT-NO DELIMITED BY SIZE
                  ": " DELIMITED BY SIZE
               INTO STMT-LINE WITH POINTER PTR
           END-STRING
           DISPLAY "after the account, PTR = " PTR
           STRING FIRST-NAME DELIMITED BY SPACE
                  " " DELIMITED BY SIZE
                  LAST-NAME DELIMITED BY SPACE
               INTO STMT-LINE WITH POINTER PTR
           END-STRING
           DISPLAY "after the name,    PTR = " PTR
           DISPLAY "[" STMT-LINE "]"
           MOVE SPACES TO SHORT-NAME
           MOVE 1 TO PTR
           STRING FIRST-NAME DELIMITED BY SPACE
                  " " DELIMITED BY SIZE
                  LAST-NAME DELIMITED BY SPACE
               INTO SHORT-NAME WITH POINTER PTR
               ON OVERFLOW
                   DISPLAY "8-byte receiver: overflow, PTR = " PTR
               NOT ON OVERFLOW
                   DISPLAY "8-byte receiver: it fits, PTR = " PTR
           END-STRING
           DISPLAY "[" SHORT-NAME "]"
           STOP RUN.
The POINTER after each STRING Python · pointer_session.py
from session import files, run

# STRING WITH POINTER: two statements that build one line, then a name that does not fit an 8-byte receiver.
files("pointer.cob")
run("""
cobc -x pointer.cob && ./pointer
""")

Output

$ cobc -x pointer.cob && ./pointer
after the account, PTR = 09
after the name,    PTR = 20
[AC0417: ANITA DESAI           ]
8-byte receiver: overflow, PTR = 09
[ANITA DE]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 pointer_session.py

PTR was 9 after the account, its colon and a space, 8 characters, and 20 after the 11 characters of the name. The 8-byte receiver took ANITA DE and stopped with PTR at 9, one past its end. Follow the pointer through the three statements:

Interactive · stage by stage Where the POINTER stands after each STRING Step through the two STRING statements that build the line, then the one that runs out of room.
Where the POINTER stands after each STRING, step 5 of 5. Showing PTR = 1, AC0417 and a colon (PTR moves to 09), ANITA DESAI (PTR moves to 20), 8-byte SHORT-NAME (ANITA DE, PTR = 09), ON OVERFLOW. In focus: ON OVERFLOW.PTR = 1the first byte of the lineAC0417 and a colonPTR moves to 09ANITA DESAIPTR moves to 208-byte SHORT-NAMEANITA DE, PTR = 09ON OVERFLOWno room for the rest

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The start. MOVE 1 TO PTR before the first STRING, so copying starts at byte 1 of STMT-LINE. STRING never clears its receiver, so the program moved spaces to it first.
  2. The account. AC0417 and ": " are copied whole, 8 characters, and PTR moves past them to 9, as the session printed.
  3. The name. The second STRING starts where the first one stopped. ANITA, a space and DESAI are 11 characters, so PTR ends at 20, and the line reads AC0417, a colon, a space and ANITA DESAI.
  4. A receiver that is too short. The third STRING puts the same name into SHORT-NAME, 8 bytes, starting at PTR = 1. After ANITA DE the receiver is full and PTR is 9, one past its end.
  5. The overflow. STRING stops copying, and because the statement has ON OVERFLOW, its message runs. Without the phrase, the name would be cut to ANITA DE with no sign that anything was lost.

Without ON OVERFLOW the short receiver would still hold ANITA DE, with nothing to say that the rest of the name was lost. Every STRING whose pieces could be longer than its receiver needs the phrase, and the pointer’s value after an overflow says how far the copy got.

Key idea

Clear the receiver before a STRING, give literals DELIMITED BY SIZE, and code ON OVERFLOW wherever the pieces might not fit: STRING itself reports none of these problems.

Checkpoint

Check: STRING and its DELIMITED BY phrases · The STRING receiver, POINTER and ON OVERFLOW

  1. Question 1 of 2 STRING CITY DELIMITED BY SPACE ", " CODE-NO DELIMITED BY SPACE INTO OUT-LINE, with CITY holding PUNE and CODE-NO holding 411001. What does OUT-LINE start with?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: PUNE,411001

    A sending item is copied up to the first character that matches its delimiter. In names.cob the literal was a single space and gave nothing; here the comma before the space survives.

  2. Question 2 of 2 PTR starts at 1, and STRING copies AC0417 and ": " DELIMITED BY SIZE into a 30-byte line WITH POINTER PTR. What is PTR afterwards?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 9

    Six characters of the account and two of ": " are eight, so the pointer moves past them to 9, where the next STRING with the same pointer continues, as the session printed.

UNSTRING splits at each delimiter and keeps what lies between

UNSTRING is STRING in reverse: it reads one sending item and moves the pieces between delimiters into receiving fields, in order. COUNT IN after a receiving field receives the number of characters examined for it; IBM’s reference adds that “the delimiters are not included in this count”. Here a comma-separated payment line, with a branch that nobody entered and an amount typed after two spaces, is split into five fields:

split.cob: five fields and their counts COB · split.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. SPLIT.
      * A comma-separated payment line split into five fields: an
      * empty branch, an amount with leading spaces, and a count of
      * the characters each field received.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  CSV-LINE     PIC X(40)
               VALUE "AC0417,ANITA DESAI,,PUNE,  250000".
       01  F-ACCT       PIC X(6).
       01  F-NAME       PIC X(12).
       01  F-BRANCH     PIC X(6).
       01  F-CITY       PIC X(6).
       01  F-AMOUNT     PIC X(9).
       01  C-ACCT       PIC 99.
       01  C-NAME       PIC 99.
       01  C-BRANCH     PIC 99.
       01  C-CITY       PIC 99.
       01  C-AMOUNT     PIC 99.
       01  FIELDS       PIC 99 VALUE 0.
       PROCEDURE DIVISION.
           UNSTRING CSV-LINE DELIMITED BY ","
               INTO F-ACCT   COUNT IN C-ACCT
                    F-NAME   COUNT IN C-NAME
                    F-BRANCH COUNT IN C-BRANCH
                    F-CITY   COUNT IN C-CITY
                    F-AMOUNT COUNT IN C-AMOUNT
               TALLYING IN FIELDS
           END-UNSTRING
           DISPLAY "account [" F-ACCT "]  " C-ACCT " characters"
           DISPLAY "name    [" F-NAME "]  " C-NAME " characters"
           DISPLAY "branch  [" F-BRANCH "]  " C-BRANCH " characters"
           DISPLAY "city    [" F-CITY "]  " C-CITY " characters"
           DISPLAY "amount  [" F-AMOUNT "]  " C-AMOUNT " characters"
           DISPLAY "receiving fields used: " FIELDS
           STOP RUN.

The session also runs a second program, spaces.cob, which the next section explains:

One payment line, then names and codes Python · split_session.py
from session import files, run

# UNSTRING on a comma-separated line, then on a name with runs of spaces and on a line with one field too many.
files("split.cob", "spaces.cob")
run("""
cobc -x split.cob && ./split
cobc -x spaces.cob && ./spaces
""")

Output

$ cobc -x split.cob && ./split
account [AC0417]  06 characters
name    [ANITA DESAI ]  11 characters
branch  [      ]  00 characters
city    [PUNE  ]  04 characters
amount  [  250000 ]  15 characters
receiving fields used: 05
$ cobc -x spaces.cob && ./spaces
BY SPACE:      [RAO     ][        ][        ]
BY ALL SPACES: [RAO     ][KUMAR   ][SINGH   ]
D,W,T,X: overflow after 03 fields, PTR = 07

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 split_session.py

Step through the five fields of the payment line, with the count each one received:

Interactive · stage by stage The five fields UNSTRING took from one line Step down the column. Each stage is one receiving field and what UNSTRING put into it.
The five fields UNSTRING took from one line, step 6 of 6. Showing F-ACCT (AC0417, count 06), F-NAME (ANITA DESAI, count 11), F-BRANCH (spaces, count 00), F-CITY (PUNE, count 04), F-AMOUNT (" 250000 ", count 15), TALLYING IN FIELDS (FIELDS = 05). In focus: TALLYING IN FIELDS.F-ACCTAC0417, count 06F-NAMEANITA DESAI, count 11F-BRANCHspaces, count 00F-CITYPUNE, count 04F-AMOUNT" 250000 ", count 15TALLYING IN FIELDSFIELDS = 05

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. The account. UNSTRING examines the line from byte 1 to the first comma and moves AC0417 into F-ACCT. COUNT IN C-ACCT receives 6, the characters examined, without the comma.
  2. The name. The next piece is ANITA DESAI, 11 characters. The space inside it is data, because only the comma delimits.
  3. An empty field. Two commas follow each other, so the piece between them is empty. F-BRANCH is filled with spaces and its count is 0, a branch that nobody entered.
  4. The city. PUNE goes into F-CITY with a count of 4.
  5. The last field. After the last comma there is no delimiter, so the piece runs to the end of the 40-byte line, 15 characters. F-AMOUNT keeps the first 9 of them, the two leading spaces included, and loses the trailing spaces without a message.
  6. The tally. TALLYING IN FIELDS adds the number of receiving fields acted on to its starting value, 0, so FIELDS ends at 5. Comparing it with the number of fields the record should have is a cheap check on every line.

Three results are worth remembering. The two commas in a row produced an empty F-BRANCH: IBM’s reference says that without ALL the receiving field “is filled with spaces or zeros”, so an empty field is not an error. The leading spaces of the amount stayed in F-AMOUNT, because UNSTRING moves the characters it finds and trims nothing. And the last piece ran to the end of the 40-byte line, 15 characters, of which the 9-byte F-AMOUNT kept the first 9.

Check the pieces before you use them

An empty field and an amount with spaces in front are both normal results of UNSTRING. Test each field before it goes into arithmetic: for spaces, for its count against the field’s length, and for valid characters with the checks of the next lesson.

ALL SPACES, TALLYING IN and ON OVERFLOW say how a line was cut

A delimiter of one space treats every space as the end of a field, so a run of spaces makes empty fields. spaces.cob splits RAO, three spaces, KUMAR, two spaces and SINGH both ways. DELIMITED BY SPACE gave RAO and two empty fields; DELIMITED BY ALL SPACES gave RAO, KUMAR and SINGH, because, in IBM’s words, “multiple contiguous occurrences of any delimiters are treated as if there were only one occurrence”.

The last line of the session splits D,W,T,X into three receiving fields. Four values do not fit three fields, and IBM lists this case among the overflow conditions: all receiving fields “have been acted upon and the sending field still contains unexamined character positions”. ON OVERFLOW ran with TALLYING IN at 3 and the pointer at 7, the position of X, the first character no field received.

UNSTRING phrase What it says In the sessions
COUNT IN Characters examined for one field 15 for the last field, more than its 9 bytes
TALLYING IN Receiving fields acted on, added to the starting value 5 fields for the payment line
DELIMITED BY ALL SPACES A run of spaces is one delimiter RAO, KUMAR and SINGH
ON OVERFLOW Data left over, or a pointer out of range D,W,T,X stopped at 7

Key idea

UNSTRING reports what it did instead of failing: compare TALLYING IN with the number of fields the record should have, compare COUNT IN with each field’s length, and code ON OVERFLOW for lines with too many fields.

Checkpoint

Check: Splitting a line with UNSTRING · ALL SPACES, TALLYING IN and ON OVERFLOW

  1. Question 1 of 2 UNSTRING the line AC0417,ANITA DESAI,,PUNE, 250000 DELIMITED BY "," into five fields. What does the third field, F-BRANCH, receive?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Spaces, with a count of 0

    The session printed branch with a count of 00. A program that needs a branch must test for spaces after the UNSTRING.

  2. Question 2 of 2 NAME-LINE holds RAO, three spaces, KUMAR, two spaces and SINGH. Which delimiter puts RAO, KUMAR and SINGH into three fields?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: DELIMITED BY ALL SPACES

    Text typed with runs of spaces needs ALL SPACES; a file with one comma between fields needs a single delimiter, so that an empty field stays visible.

Reference modification names bytes by position and length

Fixed-width records put each value at a known position, and reference modification: Naming part of a data item by position and length, ITEM(start:length), counted in bytes from 1. ITEM(start:) runs to the end of the item. reads or writes such a value in place: TXN-LINE(4:6) is the 6 bytes of TXN-LINE that start at byte 4. IBM’s reference says “the leftmost position is assigned the ordinal number one”, so there is no byte 0, and when the length is left out the slice runs “up to and including the rightmost character position” of the item. This record holds a transaction line of 24 bytes and, after it, a branch code:

refmod.cob: slices of a 24-byte transaction line COB · refmod.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. REFMOD.
      * Reference modification on a fixed-width transaction line:
      * item(start:length) counts bytes from 1, and item(start:)
      * runs to the end of the item.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  TXN-REC.
           05  TXN-LINE     PIC X(24)
                   VALUE "TXNAC0417PUNE  00125075".
           05  BRANCH-CODE  PIC X(6) VALUE "BR0042".
       01  START-POS    PIC 99 VALUE 20.
       01  SLICE-LEN    PIC 99 VALUE 10.
       PROCEDURE DIVISION.
           DISPLAY "type    (1:3)   [" TXN-LINE(1:3) "]"
           DISPLAY "account (4:6)   [" TXN-LINE(4:6) "]"
           DISPLAY "city    (10:6)  [" TXN-LINE(10:6) "]"
           DISPLAY "amount  (16:)   [" TXN-LINE(16:) "]"
           MOVE "MUMBAI" TO TXN-LINE(10:6)
           DISPLAY "after the MOVE  [" TXN-LINE "]"
           DISPLAY "(20:10) from variables ["
               TXN-LINE(START-POS:SLICE-LEN) "]"
           MOVE ALL "*" TO TXN-LINE(START-POS:SLICE-LEN)
           DISPLAY "BRANCH-CODE after a MOVE there [" BRANCH-CODE "]"
           STOP RUN.
Slices, a MOVE into a slice, and a slice past the end Python · refmod_session.py
from session import files, run

# Substrings of a 24-byte line, a MOVE into the middle of it, and a slice that runs past its end: built normally, then
# with -debug, then written with constants, which cobc checks at compile time.
files("refmod.cob", "outside.cob")
run("""
cobc -x refmod.cob && ./refmod
!cobc -x -debug -o refmod-debug refmod.cob && ./refmod-debug
!cobc -x outside.cob
""")

Output

$ cobc -x refmod.cob && ./refmod
type    (1:3)   [TXN]
account (4:6)   [AC0417]
city    (10:6)  [PUNE  ]
amount  (16:)   [00125075 ]
after the MOVE  [TXNAC0417MUMBAI00125075 ]
(20:10) from variables [5075 BR004]
BRANCH-CODE after a MOVE there [*****2]
$ cobc -x -debug -o refmod-debug refmod.cob && ./refmod-debug
type    (1:3)   [TXN]
account (4:6)   [AC0417]
city    (10:6)  [PUNE  ]
amount  (16:)   [00125075 ]
after the MOVE  [TXNAC0417MUMBAI00125075 ]
libcob: refmod.cob:21: error: length of 'TXN-LINE' out of bounds: 10, starting at: 20, maximum: 24

 Last statement of "REFMOD" was DISPLAY at line 21 of refmod.cob
	ENTRY REFMOD at refmod.cob:15
 Started by ./refmod-debug
[exit status 1]
$ cobc -x outside.cob
outside.cob:8: error: length of 'TXN-LINE' out of bounds: 10
    6 |        01  TXN-LINE     PIC X(24).
    7 |        PROCEDURE DIVISION.
    8 >            DISPLAY TXN-LINE(20:10)
    9 |            STOP RUN.
   10 | <EOF>
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 refmod_session.py

The first four lines read the type, the account, the city and the amount; (16:) took bytes 16 to 24, the amount and the space after it. MOVE “MUMBAI” TO TXN-LINE(10:6) wrote over bytes 10 to 15 and left every other byte as it was. Step through the slices:

Interactive · step by step Which bytes each reference modification names Step through the slices of refmod.cob. The second line of each box is the byte range of TXN-LINE it covers.
Which bytes each reference modification names, step 6 of 6. Showing Type (1:3), Account (4:6), City (10:6) (bytes 10-15: MUMBAI), Amount (16:), BRANCH-CODE. In focus: Amount (16:).next in storageType (1:3)bytes 1-3: TXNAccount (4:6)bytes 4-9: AC0417City (10:6)bytes 10-15: MUMBAIAmount (16:)bytes 16-24: 00125075BRANCH-CODEnext field: BR0042

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Counting from 1. TXN-LINE(1:3) starts at byte 1 and takes 3 bytes, TXN. IBM's reference gives the leftmost position the ordinal number one, so there is no byte 0.
  2. Start and length. (4:6) is the account, AC0417, and (10:6) the city, PUNE and two spaces. The length counts bytes, so trailing spaces come with the slice.
  3. To the end. (16:) leaves out the length, so the slice runs to the end of the item, bytes 16 to 24, the amount and the space after it.
  4. Writing into a slice. MOVE "MUMBAI" TO TXN-LINE(10:6) replaces bytes 10 to 15 and nothing else, so the line reads TXNAC0417MUMBAI00125075.
  5. Past the end. (START-POS:SLICE-LEN) with 20 and 10 asks for bytes 20 to 29 of a 24-byte item. The normal build printed 5075, a space and BR004, five bytes of the next field, and a MOVE of asterisks to that slice left BRANCH-CODE as *****2, with no error.
  6. With the checks. The -debug build stopped at that DISPLAY with length of 'TXN-LINE' out of bounds and exit status 1. The same slice written with constants did not even compile.

A slice can be used wherever a data item can: as a sending item, as a receiver, in a comparison. The start and the length may be data items or arithmetic expressions, which is what makes the bounds matter.

Bytes, not letters

Every position and length here counts bytes, and so do STRING, UNSTRING and their counts. RFC 3629 encodes each character outside ASCII in two to four bytes of UTF-8, so a slice of 10 bytes can end in the middle of a letter of a name written in such a script. A later lesson of this track handles text of that kind.

A slice past the end reads the next field unless checks are on

The last slice of refmod.cob takes its start and length from data items, 20 and 10, which asks for bytes 20 to 29 of a 24-byte item. The normal build printed 5075 BR004: four bytes of the amount, the space after it, and five bytes of BRANCH-CODE, the field that follows TXN-LINE in storage. Nothing failed. The MOVE of asterisks to the same slice wrote over BRANCH-CODE just as quietly: it read *****2 afterwards.

The -debug build stopped at the same DISPLAY with length of 'TXN-LINE' out of bounds: 10, starting at: 20, maximum: 24 and status 1. That check is EC-BOUND-REF-MOD in cobc –list-exceptions, and GnuCOBOL’s NEWS shows how -fec= turns on exception checks one by one, for a production build that keeps this check without everything else -debug adds. IBM’s compiler has the same kind of check in its SSRANGE option, which checks “the starting position and length value (if specified) do not reference an area beyond the end” of the item, and whose documented default is NOSSRANGE.

Computing a slice without checking it

Wrong: MOVE TXN-LINE(START-POS:SLICE-LEN) TO OUT-FIELD with START-POS and SLICE-LEN read from input or computed. Right: test that START-POS is at least 1 and that START-POS + SLICE-LEN - 1 is at most the item’s length before the MOVE, and build tests with -debug so that a missed case stops the run.

outside.cob wrote the slice with constants, TXN-LINE(20:10), and cobc refused to compile it: a range made of constants is checked when the program is built, while a range made of variables can be checked only while it runs.

Key idea

A slice is checked only when its numbers are constants or the build switches on a run-time check; any other slice that runs past its item reads or overwrites the bytes that follow it.

Checkpoint

Check: Reference modification · Slices past the end of an item

  1. Question 1 of 2 TXN-LINE is 24 bytes. Which bytes does TXN-LINE(16:) name?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Bytes 16 to 24

    refmod.cob printed (16:) as the amount and the space after it, bytes 16 to 24.

  2. Question 2 of 2 TXN-LINE(START-POS:SLICE-LEN) runs with 20 and 10 on a 24-byte TXN-LINE, in a build without -debug. What happens?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It reads bytes 20 to 29, the last five of TXN-LINE and the first five of the next field, without an error

    A MOVE to the same slice wrote asterisks over BRANCH-CODE. Test the bounds in the code, and build tests with -debug or EC-BOUND-REF-MOD.

Practise: cut and join a payment line

The questions below are about the STRING, UNSTRING and slices of this lesson’s programs. Decide what each statement leaves in its receiving field, then compare with the recorded sessions; the questions begin where your answers have been weakest:

Practice that adapts to you

Practise this lesson

18 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 18 What does DELIMITED BY SIZE do in a STRING statement?

    Choose one answer.

    Show the answer: Question 1 of 18

    Answer: It copies the whole sending item

    Literals and fixed codes that must be copied as they are take DELIMITED BY SIZE.

  2. Question 2 of 18 In STRING A DELIMITED BY SPACE "-" B C DELIMITED BY "*" INTO OUT, which delimiter applies to the literal "-"?

    Choose one answer.

    Show the answer: Question 2 of 18

    Answer: "*", the next DELIMITED BY phrase after it

    Read a STRING backwards from each DELIMITED BY phrase: it applies to every sending item up to the previous phrase.

  3. Question 3 of 18 A name field may hold a two-word first name, such as ANNA MARIA. What happens when it is joined with DELIMITED BY SPACE, and what fits better?

    Choose one answer.

    Show the answer: Question 3 of 18

    Answer: Only ANNA is copied; delimit by two spaces or trim the field instead

    DELIMITED BY SPACE fits fields that never contain a space. For names, trimming the trailing spaces with FUNCTION TRIM, taught in the next lesson, keeps the inner spaces.

  4. Question 4 of 18 FULL-NAME holds ANITA DESAI. What does it hold after STRING "RAVI" DELIMITED BY SIZE INTO FULL-NAME?

    Choose one answer.

    Show the answer: Question 4 of 18

    Answer: RAVIA DESAI

    The session printed RAVIA DESAI. Move SPACES to a receiver before building it from scratch.

  5. Question 5 of 18 When does ON OVERFLOW run in a STRING statement?

    Choose one answer.

    Show the answer: Question 5 of 18

    Answer: When the pointer goes below 1 or past the end of the receiver, so not everything could be copied

    The 8-byte SHORT-NAME overflowed after ANITA DE with PTR at 9, one past its end.

  6. Question 6 of 18 Two STRING statements share POINTER PTR to build one line, and the second one gives PTR = 20. Why does the line read AC0417, a colon, a space and ANITA DESAI?

    Choose one answer.

    Show the answer: Question 6 of 18

    Answer: The second STRING started at byte 9, where the first one left PTR

    Set the pointer to 1 once, before the first STRING, and let every later STRING move it on.

  7. Question 7 of 18 What does COUNT IN hold after UNSTRING has filled a receiving field?

    Choose one answer.

    Show the answer: Question 7 of 18

    Answer: The number of characters examined for that field, without the delimiter

    F-ACCT received AC0417 with a count of 6, and F-AMOUNT a count of 15 for 9 bytes.

  8. Question 8 of 18 The amount piece of the line is two spaces and 250000. What does F-AMOUNT, PIC X(9), hold after UNSTRING?

    Choose one answer.

    Show the answer: Question 8 of 18

    Answer: The two spaces, 250000 and one more space

    The session printed the amount between brackets with its two leading spaces. Convert it only after checking it.

  9. Question 9 of 18 Why was COUNT IN 15 for the last field of the 40-byte line, although its value is 8 characters?

    Choose one answer.

    Show the answer: Question 9 of 18

    Answer: With no delimiter after it, the last piece runs to the end of the sending item, trailing spaces included

    A count larger than the receiving field warns that something was cut. Here only spaces were lost, but the same check catches a value that is too long.

  10. Question 10 of 18 What does TALLYING IN FIELDS hold after an UNSTRING that started with FIELDS = 0 and filled five receiving fields?

    Choose one answer.

    Show the answer: Question 10 of 18

    Answer: 5

    Because it adds to the starting value, move 0 to the tally before each UNSTRING.

  11. Question 11 of 18 UNSTRING D,W,T,X DELIMITED BY "," into three fields WITH POINTER PTR. Which fields are filled, and why does ON OVERFLOW run?

    Choose one answer.

    Show the answer: Question 11 of 18

    Answer: D, W and T; X is left unexamined when all three fields have been acted on

    Without ON OVERFLOW, the extra field would be lost without a trace. With it, the program can reject the line.

  12. Question 12 of 18 A file of comma-separated payments must have exactly five fields per line. Which checks belong after each UNSTRING?

    Choose every answer that is right.

    Show the answer: Question 12 of 18

    Answer:

    • ON OVERFLOW did not run
    • Each COUNT IN is no larger than its field's length
    • TALLYING IN is 5

    The three checks come from what UNSTRING reports. Which fields may be empty depends on the file's own rules.

  13. Question 13 of 18 What does TXN-LINE(4:6) name?

    Choose one answer.

    Show the answer: Question 13 of 18

    Answer: The 6 bytes that start at byte 4, bytes 4 to 9

    refmod.cob printed AC0417 for (4:6).

  14. Question 14 of 18 What does MOVE "MUMBAI" TO TXN-LINE(10:6) change?

    Choose one answer.

    Show the answer: Question 14 of 18

    Answer: Bytes 10 to 15 of TXN-LINE, and nothing else

    The line read TXNAC0417MUMBAI00125075 afterwards: the type, the account and the amount were untouched.

  15. Question 15 of 18 A name in a Devanagari script is stored in UTF-8 in a PIC X field. What can NAME-IN(1:10) do?

    Choose one answer.

    Show the answer: Question 15 of 18

    Answer: End in the middle of a letter, because the positions count bytes and each such letter takes several

    Text in scripts outside ASCII needs character-aware handling, which a later lesson of this track covers.

  16. Question 16 of 18 Which out-of-range slice does GnuCOBOL catch when the program is compiled?

    Choose one answer.

    Show the answer: Question 16 of 18

    Answer: One written with constants, such as TXN-LINE(20:10) on a 24-byte item

    Bounds computed at run time need the code's own tests, or a run-time check such as -debug.

  17. Question 17 of 18 The same out-of-range slice runs in a -debug build. What happens?

    Choose one answer.

    Show the answer: Question 17 of 18

    Answer: The run stops at that statement with an out-of-bounds message and status 1

    EC-BOUND-REF-MOD is the check -debug turned on; IBM's compiler has the SSRANGE option for the same purpose.

  18. Question 18 of 18 Production builds do not use -debug, and a slice's start comes from an input record. What protects the program?

    Choose one answer.

    Show the answer: Question 18 of 18

    Answer: Code that tests the start and the length against the item's size before the slice is used

    Build tests with -debug or EC-BOUND-REF-MOD so that a missed case stops the run, and keep explicit tests for values that come from outside.

Then build a small parser where GnuCOBOL is installed:

Parse a pipe-delimited line and rebuild it

Split the line TXN|AC0001|PUNE|1250.75 with UNSTRING … DELIMITED BY “|” into four fields, with COUNT IN for each and TALLYING IN for the line, then rebuild it as a fixed-width record with STRING and DELIMITED BY SIZE. Run it again on TXN|AC0001||1250.75, which has an empty field, and on TXN|AC0001|PUNE|1250.75|X, which has one field too many, and make the program report both.

Interview questions

Warm-up (fresher to 2–5 years): why does STRING FIRST-NAME DELIMITED BY SPACE “ “ LAST-NAME DELIMITED BY SPACE drop the space?

A DELIMITED BY phrase applies to every sending item before it that has no phrase of its own, so the literal “ “ is copied as if it were written “ “ DELIMITED BY SPACE. Copying stops at the first character that matches the delimiter, and the literal’s only character is a space, so it contributes nothing, and the result is ANITADESAI instead of ANITA DESAI. The fix is to give the literal its own phrase, “ “ DELIMITED BY SIZE, which copies it whole. The general rule: every literal in a STRING gets DELIMITED BY SIZE, and the receiver is cleared with MOVE SPACES first, because STRING writes only the positions it fills.

Warm-up (fresher to 2–5 years): what does NAME(3:4) mean, and what happens when the slice runs past the end of NAME?

NAME(3:4) is reference modification: the 4 bytes of NAME that start at byte 3, so bytes 3 to 6, counted from 1. It can be read or written like any data item, and NAME(3:) without a length runs to the end of the item. When the start or the length are constants that fall outside the item, GnuCOBOL refuses to compile the program. When they come from data items, nothing checks them in a normal build: the slice reads or overwrites the bytes that follow NAME in storage. A build with -debug, or with the EC-BOUND-REF-MOD check, stops the run instead, and IBM’s compiler has the SSRANGE option for the same check.

Two harder questions follow: a payment file whose fields shift, and a report line that shows the wrong amount. Choose your first step before you open each one:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Mid to senior

    A partner now sends payments as comma-separated lines, which the program splits with UNSTRING DELIMITED BY ",". Since the switch, some payments post to the wrong branch and a few amounts arrive cut short, yet the batch reports no errors. How do you find the bad lines, and what do you change?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    A statement program builds each detail line with STRING into PRINT-LINE. For some customers the line ends with an extra amount that belongs to the customer printed before them, although no statement moves that amount there. What is happening, and how do you fix and test it?

    Model answer, Pro. First step and follow-ups

Organisations named on this page

MySmartCoPilot has no tie to IBM, the GnuCOBOL project or the IETF. The STRING, UNSTRING and SSRANGE rules here are cited from IBM’s documentation, the byte counts of UTF-8 from RFC 3629, and the outputs are runs of GnuCOBOL 3.2.

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. STRING FIRST-NAME DELIMITED BY SPACE " " LAST-NAME DELIMITED BY SPACE gave ANITADESAI. Why?

    A DELIMITED BY phrase covers every sending item before it that has none of its own, so the literal space is cut at a space and adds nothing. Give each literal DELIMITED BY SIZE.

  2. Does STRING clear its receiving item first?

    No, it writes only the positions it fills: RAVI over ANITA DESAI left RAVIA DESAI. Move SPACES to the receiver before a STRING that builds it from scratch.

  3. What does the POINTER item of a STRING hold afterwards?

    The position where copying would go on. It must start at 1 or more, and STRING moves it past each character it copies, so two STRINGs with one pointer build a line in pieces.

  4. When does ON OVERFLOW run in a STRING?

    When the pointer is below 1 or goes past the end of the receiver, so not everything can be copied. Without the phrase the receiver keeps what fitted, and nothing reports the loss.

  5. What does UNSTRING put into a field when two delimiters are side by side?

    Spaces, or zeros for a numeric field: an empty field is not an error. UNSTRING also keeps leading spaces, because it trims nothing.

  6. How do DELIMITED BY SPACE and DELIMITED BY ALL SPACES differ?

    With SPACE every single space ends a field, so a run of spaces makes empty fields. ALL SPACES treats a run of spaces as one delimiter.

  7. What do COUNT IN and TALLYING IN report in an UNSTRING?

    COUNT IN: the characters examined for one field, delimiters left out. TALLYING IN: how many receiving fields were acted on. Compare them with each field's length and the expected number of fields.

  8. What do TXN-LINE(4:6) and TXN-LINE(16:) name?

    The 6 bytes of TXN-LINE that start at byte 4, counted from 1; with no length, the bytes from 16 to the end of the item. A MOVE into a slice changes only those bytes.

Key takeaways

  • STRING copies each sending item up to its delimiter, and a DELIMITED BY phrase covers every item before it, so a literal needs DELIMITED BY SIZE or it may contribute nothing, as the space of ANITADESAI did.
  • STRING writes only the positions it fills and moves POINTER past each character, so clear the receiver first and code ON OVERFLOW where the pieces may not fit.
  • UNSTRING fills an empty field with spaces, keeps leading spaces, runs the last field to the end of the line and reports what it did with COUNT IN, TALLYING IN and ON OVERFLOW rather than failing.
  • Reference modification counts bytes from 1, ITEM(start:) runs to the end, and a MOVE to a slice changes only those bytes.
  • A slice with variable bounds that runs past its item reads or overwrites the next field unless -debug, EC-BOUND-REF-MOD or IBM’s SSRANGE checks it; constant bounds are checked by the compiler.

Terms in this lesson

delimiter
A character or string that marks where a piece of text ends. STRING copies each sending item up to its delimiter, and UNSTRING splits its sending item at each one.
reference modification
Naming part of a data item by position and length, ITEM(start:length), counted in bytes from 1. ITEM(start:) runs to the end of the item.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.