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.
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:
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. 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:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- 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.
- 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.
- The last name. LAST-NAME gives DESAI, the characters before its first space.
- 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.
- 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:
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. 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:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- 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.
- The account. AC0417 and ": " are copied whole, 8 characters, and PTR moves past them to 9, as the session printed.
- 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.
- 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.
- 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
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:
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:
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:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- 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.
- The name. The next piece is ANITA DESAI, 11 characters. The space inside it is data, because only the comma delimits.
- 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.
- The city. PUNE goes into F-CITY with a count of 4.
- 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.
- 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
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:
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. 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:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- 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.
- 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.
- 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.
- Writing into a slice. MOVE "MUMBAI" TO TXN-LINE(10:6) replaces bytes 10 to 15 and nothing else, so the line reads TXNAC0417MUMBAI00125075.
- 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. - With the checks. The -debug build stopped at that DISPLAY with
length of 'TXN-LINE' out of boundsand 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
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.
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.
-
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?
-
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?
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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
- Enterprise COBOL for z/OS 6.5: STRING statement (IBM)
- Enterprise COBOL for z/OS 6.5: UNSTRING statement (IBM)
- Enterprise COBOL for z/OS 6.5: Reference modification (IBM)
- Enterprise COBOL for z/OS 6.5: SSRANGE compiler option (IBM)
- GnuCOBOL 3.2 NEWS (GnuCOBOL project (GNU))
- RFC 3629: UTF-8, a transformation format of ISO 10646 (Internet Engineering Task Force (IETF))
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress