Strings: STRING, UNSTRING and INSPECT
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; a 4th edition is upcoming), GnuCOBOL 3.2 and IBM
Enterprise COBOL for z/OS 6.5, checked on 2026-10-20. Every program below was compiled with cobc -x -Wall (GnuCOBOL 3.2.0)
and run. The ones presented as working compiled without warnings (also under -std=ibm), and the deliberately wrong ones
show the real diagnostics. The examples use the Circulation library scenario of this section: member names and book
titles from the copybooks MEMBREC and BOOKREC, and a CSV file of new books exported from a PC tool.
COBOL has no string type and no built-in split, replace or substring functions in the style of other languages. Text is
a fixed-length PIC X(n) item, and the work is done by three statements and one notation:
-
reference modification,
item(start:length), which names a part of an item; -
STRING, which joins pieces into one item (concatenation); -
UNSTRING, which splits one item into several (tokenising); -
INSPECT, which counts, replaces or translates characters in place.
Section "Text intrinsic functions" adds TRIM, UPPER-CASE, LOWER-CASE, REVERSE and the GnuCOBOL functions CONCAT and
SUBSTITUTE, and the last section puts the pieces together to parse a CSV line. The fixed-length nature of every item
explains most of the pitfalls on this page: the receiving field is never shortened or cleared, trailing spaces are data,
and a count of "characters" includes the padding. Moving whole items is on
MOVE and Data Movement, the picture symbols of the
items used here are on The PICTURE Clause, every intrinsic function
is on Intrinsic Functions, and national and UTF-8 text is on
National Data, UTF-8 and Code Pages.
Reference modification
Reference modification selects a substring of a data item by position. The notation is the item name followed by
(leftmost-character-position : length), with a colon and no spaces needed. The length is optional: item(start:) runs to
the end of the item.
data-name (start : [length])
FUNCTION function-name (arguments) (start : [length])
The rules, as the Enterprise COBOL Language Reference states them:
-
Positions count from 1.
startmust be a positive nonzero integer no greater than the number of characters in the item, andlength, when given, must be a positive nonzero integer withstart + length - 1not beyond the end of the item. Both are arithmetic expressions, soBOOK-TITLE(WS-START + 4:WS-LEN)is valid. -
The item can be qualified or subscripted; the reference modifier is evaluated right after the subscript, so
WS-FIELD(WS-N)(WS-OUT-LEN:1)selects one character of one table element. -
It works on an elementary item or on a group. For a numeric or edited
DISPLAYitem the characters are treated as if the item were alphanumeric, soWS-ID(3:2)on aPIC 9(6)item is the text34of123456, not a number. -
The result is an elementary item. It can be a sending field, a receiving field, an operand of
STRING,UNSTRINGandINSPECT, or the argument of a function. A reference-modified result of a function is allowed too:FUNCTION UPPER-CASE(WS-TITLE)(1:5). -
For
NATIONALandDISPLAY-1items the positions are character positions of two bytes each, not byte offsets.
The program below applies reference modification to the Circulation book record: a prefix of the ISBN, a computed window of the title, a target that only changes the named bytes, and a whole group.
IDENTIFICATION DIVISION.
PROGRAM-ID. REFMOD1.
* Reference modification on a BOOKMAST record.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY BOOKREC.
01 WS-START PIC 9(2) VALUE 1.
01 WS-LEN PIC 9(2) VALUE 4.
01 WS-CODE PIC X(3).
PROCEDURE DIVISION.
MAIN-PARA.
MOVE 123456 TO BOOK-ID
MOVE "9780000000028" TO BOOK-ISBN
MOVE "COBOL BASICS" TO BOOK-TITLE
DISPLAY "ISBN prefix : " BOOK-ISBN(1:3)
DISPLAY "ISBN check : " BOOK-ISBN(13:1)
DISPLAY "Title tail : [" BOOK-TITLE(6:) "]"
DISPLAY "Computed : [" BOOK-TITLE(WS-START:WS-LEN) "]"
ADD 1 TO WS-START
DISPLAY "Next window : [" BOOK-TITLE(WS-START + 4:WS-LEN)
"]"
MOVE BOOK-TITLE(1:3) TO WS-CODE
DISPLAY "Code : " WS-CODE
* As a target: only the named bytes change.
MOVE "XYZ" TO BOOK-TITLE(1:3)
DISPLAY "After MOVE : [" BOOK-TITLE "]"
MOVE ALL "-" TO BOOK-TITLE(13:)
DISPLAY "After fill : [" BOOK-TITLE "]"
* A whole group can be reference-modified too.
DISPLAY "Record 1:6 : " BOOK-RECORD(1:6)
DISPLAY "Length group : " FUNCTION LENGTH(BOOK-RECORD)
STOP RUN.
ISBN prefix : 978
ISBN check : 8
Title tail : [ BASICS ]
Computed : [COBO]
Next window : [ BAS]
Code : COB
After MOVE : [XYZOL BASICS ]
After fill : [XYZOL BASICS------------------]
Record 1:6 : 123456
Length group : 60
The compiler checks what it can at compile time. Constant positions and lengths that fall outside the item are errors, as in
this deliberately wrong program (the source lines that cobc echoes are left out):
IDENTIFICATION DIVISION.
PROGRAM-ID. RMBAD1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TITLE PIC X(10) VALUE "COBOL BASI".
PROCEDURE DIVISION.
DISPLAY WS-TITLE(11:1)
DISPLAY WS-TITLE(0:1)
DISPLAY WS-TITLE(5:9)
STOP RUN.
rmbad1.cob:7: error: offset of 'WS-TITLE' out of bounds: 11
rmbad1.cob:8: error: offset of 'WS-TITLE' out of bounds: 0
rmbad1.cob:9: error: length of 'WS-TITLE' out of bounds: 9
When the position or the length is a variable, nothing is checked by default and the statement reads whatever follows the item
in storage. The next program (also deliberately wrong) asks for six characters starting at position 8 of a 10-byte item.
Compiled normally it displays three characters of the item followed by three unrelated bytes, which here are binary zeros
(od -c shows [ A S I \0 \0 \0 ]). Compiled with -debug the same statement stops with a run-time error:
IDENTIFICATION DIVISION.
PROGRAM-ID. RMBAD2.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TITLE PIC X(10) VALUE "COBOL BASI".
01 WS-POS PIC 9(2) VALUE 8.
01 WS-LEN PIC 9(2) VALUE 6.
PROCEDURE DIVISION.
DISPLAY "[" WS-TITLE(WS-POS:WS-LEN) "]"
STOP RUN.
libcob: rmbad2.cob:9: error: length of 'WS-TITLE' out of bounds: 6, starting at: 8, maximum: 10
Enterprise COBOL gives the same protection with the SSRANGE compiler option (the Programming Guide section on checking
valid ranges is covered on Tables and OCCURS). A zero length is
invalid in Enterprise COBOL; in a test GnuCOBOL 3.2.0 accepted WS-TITLE(3:WS-LEN) with WS-LEN equal to 0 at run time,
even with -debug, and produced an empty result, so do not rely on it.
STRING: joining data
STRING copies the leading part (or all) of one or more sending items into one receiving item, one after another, and can
report when the receiver is too short.
STRING { identifier-1 | literal-1 } ...
[ DELIMITED BY { identifier-2 | literal-2 | SIZE } ] ...
INTO identifier-3
[ WITH POINTER identifier-4 ]
[ ON OVERFLOW imperative-statement-1 ]
[ NOT ON OVERFLOW imperative-statement-2 ]
[ END-STRING ]
How it works, from the Enterprise COBOL Language Reference:
-
The sending items are processed left to right in the order written. Each
DELIMITED BYapplies to the sending items written before it (up to the previousDELIMITED BY). WithDELIMITED BY SIZEthe whole item is transferred. With a literal or identifier, characters are transferred up to, but not including, the first occurrence of the delimiter, or to the end of the item when it does not occur. The delimiter itself is not copied. -
Transfer starts at the pointer position, which is 1 when there is no
POINTERphrase. After each character is placed the pointer is increased by 1, so at the end it points one position past the last character transferred. -
Only the positions that receive characters are changed. The rest of the receiver keeps its previous contents; it is not space filled. Clear it with
MOVE SPACESfirst if you need a clean result. -
The explicit pointer must be set to a value of at least 1 before the statement and, for a fixed-length receiver, no more than the receiver length. Declare it large enough for the length of the receiver plus 1.
-
Overflow occurs when the pointer is less than 1 or greater than the length of the receiver, whether at the start or while transferring. The transfer stops,
NOT ON OVERFLOWis skipped, and control goes to theON OVERFLOWstatements if there are any. Without anON OVERFLOWphrase the rest of the data is silently dropped. -
Operand rules: numeric senders must be unsigned integers (no
Pin the picture); the receiver must not be numeric, edited orJUSTIFIED; all identifiers must have the same usage and all literals must match it. A figurative constant can be a delimiter but not one that begins withALL. A sender and the receiver or the pointer must not overlap.
The program below builds a label with a literal delimiter of two spaces (the end of a name padded with spaces), shows the untouched tail of the receiver, appends in two steps with a pointer, and then makes the receiver too short.
IDENTIFICATION DIVISION.
PROGRAM-ID. STRING1.
* STRING: delimiters, POINTER and ON OVERFLOW in Circulation.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY MEMBREC.
COPY BOOKREC.
01 WS-LABEL PIC X(40).
01 WS-SHORT PIC X(20).
01 WS-PTR PIC 9(3).
01 WS-COPIES-OUT PIC 9(3).
PROCEDURE DIVISION.
MAIN-PARA.
MOVE 101 TO MEMBER-ID
MOVE "Grace Hopper" TO MEMBER-NAME
MOVE "BATCH PROCESSING" TO BOOK-TITLE
MOVE 1 TO BOOK-COPIES
* 1. Delimited by a literal and by SIZE.
MOVE ALL "*" TO WS-LABEL
STRING MEMBER-NAME DELIMITED BY " "
" borrowed " DELIMITED BY SIZE
BOOK-TITLE DELIMITED BY " "
INTO WS-LABEL
END-STRING
DISPLAY "1: [" WS-LABEL "]"
* 2. The receiving field is not space filled: clear it first.
MOVE SPACES TO WS-LABEL
STRING MEMBER-ID DELIMITED BY SIZE
"/" DELIMITED BY SIZE
BOOK-COPIES DELIMITED BY SIZE
INTO WS-LABEL
END-STRING
DISPLAY "2: [" WS-LABEL "]"
* 3. WITH POINTER appends to what is already there.
MOVE SPACES TO WS-LABEL
MOVE 1 TO WS-PTR
STRING "ID=" DELIMITED BY SIZE
MEMBER-ID DELIMITED BY SIZE
INTO WS-LABEL WITH POINTER WS-PTR
END-STRING
DISPLAY "3a pointer now " WS-PTR
STRING ";" DELIMITED BY SIZE
MEMBER-NAME DELIMITED BY " "
INTO WS-LABEL WITH POINTER WS-PTR
END-STRING
DISPLAY "3b pointer now " WS-PTR " [" WS-LABEL "]"
* 4. ON OVERFLOW: the receiver is too short.
MOVE SPACES TO WS-SHORT
MOVE 1 TO WS-PTR
STRING MEMBER-NAME DELIMITED BY " "
" / " DELIMITED BY SIZE
BOOK-TITLE DELIMITED BY " "
INTO WS-SHORT WITH POINTER WS-PTR
ON OVERFLOW
DISPLAY "4: overflow, pointer " WS-PTR
NOT ON OVERFLOW
DISPLAY "4: fits"
END-STRING
DISPLAY "4: [" WS-SHORT "]"
* 5. Without OVERFLOW the cut-off is silent.
MOVE SPACES TO WS-SHORT
STRING MEMBER-NAME DELIMITED BY " "
" / " DELIMITED BY SIZE
BOOK-TITLE DELIMITED BY " "
INTO WS-SHORT
END-STRING
DISPLAY "5: [" WS-SHORT "]"
STOP RUN.
1: [Grace Hopper borrowed BATCH PROCESSING**]
2: [000101/001 ]
3a pointer now 010
3b pointer now 023 [ID=000101;Grace Hopper ]
4: overflow, pointer 021
4: [Grace Hopper / BATCH]
5: [Grace Hopper / BATCH]
Look at the output line by line:
-
Line 1 is
Grace Hopper borrowed BATCH PROCESSINGfollowed by**: the receiver was filled with asterisks first, and the two positions the statement did not reach keep the old contents. -
Line 2 shows how a numeric item travels:
MEMBER-ID(PIC 9(6), value 101) is copied as its six digits000101, with leading zeros, andBOOK-COPIESas001. The sender is treated as characters; move the number to a numeric-edited item first if the zeros are not wanted. -
In 3a and 3b the pointer starts at 1, ends at 10 after the first statement (nine characters written) and at 23 after the second, so the second
STRINGappended after the first. -
In 4 the receiver holds 20 characters.
Grace Hopper / BATCHis exactly 20, so the rest of the title (` PROCESSING`) could not be placed: overflow was reported and the pointer stopped at 21, one past the end. -
In 5 the same statement without
ON OVERFLOWproduced the same truncated text and no sign of the problem.
Two more behaviours of GnuCOBOL 3.2.0 are worth knowing, shown by the next program. A PIC 9(4)V99 sender (not an
integer) is accepted and sent as its six digits without the decimal point, and a signed sender carries its sign in the
last digit (an overpunch, here the character r for -12 in ASCII). An unset pointer of 0 is an overflow at once and nothing is transferred.
IDENTIFICATION DIVISION.
PROGRAM-ID. STRING2.
* STRING with numeric senders and an unset POINTER.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PRICE PIC 9(4)V99 VALUE 1250.
01 WS-AMOUNT PIC S9(4) VALUE -12.
01 WS-OUT PIC X(20).
01 WS-PTR PIC 9(3) VALUE ZERO.
PROCEDURE DIVISION.
MAIN-PARA.
MOVE ALL "*" TO WS-OUT
STRING WS-PRICE DELIMITED BY SIZE INTO WS-OUT
END-STRING
DISPLAY "price : [" WS-OUT "]"
MOVE ALL "*" TO WS-OUT
STRING WS-AMOUNT DELIMITED BY SIZE INTO WS-OUT
END-STRING
DISPLAY "signed : [" WS-OUT "]"
MOVE ALL "*" TO WS-OUT
STRING "abc" DELIMITED BY SIZE INTO WS-OUT
WITH POINTER WS-PTR
ON OVERFLOW
DISPLAY "pointer 0: overflow, pointer " WS-PTR
END-STRING
DISPLAY "unset : [" WS-OUT "]"
STOP RUN.
price : [125000**************]
signed : [001r****************]
pointer 0: overflow, pointer 000
unset : [********************]
GnuCOBOL accepts both numeric senders without a diagnostic, even under -std=ibm. Enterprise COBOL’s rule is stricter: a
numeric sender must be an integer without the symbol P, so the first statement would not compile there. Move such a value
to an edited item (for example PIC Z(3)9.99) first and send that item; the result is characters you chose.
UNSTRING: splitting data
UNSTRING is the opposite of STRING. It scans one sending item for delimiters and moves the pieces into several
receiving items.
UNSTRING identifier-1
[ DELIMITED BY [ ALL ] { identifier-2 | literal-1 }
[ OR [ ALL ] { identifier-3 | literal-2 } ] ... ]
INTO { identifier-4 [ DELIMITER IN identifier-5 ]
[ COUNT IN identifier-6 ] } ...
[ WITH POINTER identifier-7 ]
[ TALLYING IN identifier-8 ]
[ ON OVERFLOW imperative-statement-1 ]
[ NOT ON OVERFLOW imperative-statement-2 ]
[ END-UNSTRING ]
The phrases, as the Language Reference defines them:
-
DELIMITED BYlists the delimiters. They can be literals or identifiers of one or more characters, joined withOR. A multi-character delimiter matches only when all its characters are contiguous and in order. WithOR, the first delimiter that matches at the current position is used; the manual’s example is"AB" OR "BC", whereABCcontains the delimiterAB. -
ALLmakes consecutive occurrences of a delimiter count as one. Without it, two delimiters in a row produce an empty receiving item (filled with spaces, or zeros for a numeric receiver). -
Without
DELIMITED BY, each receiver takes as many characters as its own size (for a numeric receiver the integer part of its size) andDELIMITER INandCOUNT INare not allowed. -
DELIMITER INstores the delimiter that ended the piece (spaces when the piece ended at the end of the sending item).COUNT INstores the number of characters examined for that piece, without the delimiter. -
WITH POINTERstarts scanning at the given character position and, when the statement ends, holds the initial value plus the number of positions examined. Initialise it to at least 1 yourself. -
TALLYING INadds the number of receiving items that were acted upon to the field, which you must initialise. -
Overflow occurs when the pointer is outside the sending item, or when all receivers have been used and the sending item still has unexamined characters. Transfer stops and
NOT ON OVERFLOWis skipped. -
Each piece is moved by the normal
MOVErules for the sending category into the receiver, so a receiver that is too short gets the piece truncated on the right. A numeric receiver (usageDISPLAYorNATIONAL, noP) receives the piece with theMOVErules for an alphanumeric sender, so the text must be digits; convert text such as15.50withFUNCTION NUMVALinstead. -
A receiver that is not reached keeps its previous contents. Clear the receivers first.
The sending item must have category alphabetic, alphanumeric, alphanumeric-edited, DBCS, national, national-edited or UTF-8.
IDENTIFICATION DIVISION.
PROGRAM-ID. UNSTR1.
* UNSTRING: DELIMITER IN, COUNT IN, TALLYING, ALL and OR.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-LINE PIC X(40).
01 WS-F1 PIC X(12).
01 WS-F2 PIC X(12).
01 WS-F3 PIC X(12).
01 WS-D1 PIC X(2).
01 WS-D2 PIC X(2).
01 WS-C1 PIC 9(2).
01 WS-C2 PIC 9(2).
01 WS-C3 PIC 9(2).
01 WS-N PIC 9(2).
01 WS-PTR PIC 9(2).
PROCEDURE DIVISION.
MAIN-PARA.
* 1. Fields, delimiters, counts and a tally.
MOVE "100,Ada;Lovelace" TO WS-LINE
MOVE ZERO TO WS-N
MOVE SPACES TO WS-F1 WS-F2 WS-F3 WS-D1 WS-D2
UNSTRING WS-LINE DELIMITED BY "," OR ";"
INTO WS-F1 DELIMITER IN WS-D1 COUNT IN WS-C1
WS-F2 DELIMITER IN WS-D2 COUNT IN WS-C2
WS-F3 COUNT IN WS-C3
TALLYING IN WS-N
END-UNSTRING
DISPLAY "1: [" WS-F1 "] [" WS-F2 "] [" WS-F3 "]"
DISPLAY " delimiters [" WS-D1 "] [" WS-D2 "]"
DISPLAY " counts " WS-C1 " " WS-C2 " " WS-C3
" tally " WS-N
* 2. Two commas in a row give an empty field ...
MOVE "A,,B" TO WS-LINE
MOVE ZERO TO WS-N
MOVE "xxxxxxxxxxxx" TO WS-F1 WS-F2 WS-F3
UNSTRING WS-LINE DELIMITED BY ","
INTO WS-F1 WS-F2 WS-F3
TALLYING IN WS-N
END-UNSTRING
DISPLAY "2: [" WS-F1 "] [" WS-F2 "] [" WS-F3 "] " WS-N
* 3. ... unless DELIMITED BY ALL folds them into one.
MOVE ZERO TO WS-N
MOVE "xxxxxxxxxxxx" TO WS-F1 WS-F2 WS-F3
UNSTRING WS-LINE DELIMITED BY ALL ","
INTO WS-F1 WS-F2 WS-F3
TALLYING IN WS-N
END-UNSTRING
DISPLAY "3: [" WS-F1 "] [" WS-F2 "] [" WS-F3 "] " WS-N
* 4. POINTER resumes where the last statement stopped.
MOVE "ADA GRACE ALAN" TO WS-LINE
MOVE 1 TO WS-PTR
UNSTRING WS-LINE DELIMITED BY ALL SPACE
INTO WS-F1 WITH POINTER WS-PTR
END-UNSTRING
DISPLAY "4a [" WS-F1 "] pointer " WS-PTR
UNSTRING WS-LINE DELIMITED BY ALL SPACE
INTO WS-F2 WITH POINTER WS-PTR
END-UNSTRING
DISPLAY "4b [" WS-F2 "] pointer " WS-PTR
* 5. More fields in the data than receivers: ON OVERFLOW.
MOVE "P,Q,R,S" TO WS-LINE
UNSTRING WS-LINE DELIMITED BY ","
INTO WS-F1 WS-F2 WS-F3
ON OVERFLOW
DISPLAY "5: overflow, data left over"
NOT ON OVERFLOW
DISPLAY "5: all consumed"
END-UNSTRING
STOP RUN.
1: [100 ] [Ada ] [Lovelace ]
delimiters [, ] [; ]
counts 03 03 32 tally 03
2: [A ] [ ] [B ] 03
3: [A ] [B ] [xxxxxxxxxxxx] 02
4a [ADA ] pointer 06
4b [GRACE ] pointer 13
5: overflow, data left over
Reading the output:
-
In 1 the delimiters were
,and;, recognised throughOR, andDELIMITER INreturned each one in thePIC X(2)field (padded with a space). The last piece ended at the end of the 40-characterWS-LINE, so itsCOUNT INis 32: eight characters ofLovelaceplus the 24 padding spaces. The receiver kept only its first 12 characters. A count that is larger than the receiver signals truncation, which the CSV program below uses. -
In 2 the double comma gave an empty second field (spaces), and the tally is 3. In 3
DELIMITED BY ALL ","folded them into one, so the third receiver was never reached: it still holds thexcharacters it had, and the tally is 2. -
In 4 the pointer is 6 after the first piece (
ADAplus the two spaces folded byALL SPACE) and 13 after the second. CallingUNSTRINGrepeatedly with a pointer is the way to walk through a line with a variable number of items. -
In 5 there were four pieces for three receivers, so the overflow branch ran.
A pointer outside the sending item is an overflow as well. In a test, UNSTRING "A,B" ... WITH POINTER WS-PTR with WS-PTR equal to 0 took the ON OVERFLOW
branch and left the pointer at 0.
INSPECT: counting, replacing and converting
INSPECT examines the characters of an item in place. It has four forms: tally, replace, both in one statement, and convert.
INSPECT identifier-1 TALLYING
{ identifier-2 FOR { CHARACTERS [ba] | { ALL | LEADING } { id | lit } [ba] } ... } ...
INSPECT identifier-1 REPLACING
{ CHARACTERS BY { id | lit } [ba]
| { ALL | LEADING | FIRST } { id | lit } BY { id | lit } [ba] } ...
INSPECT identifier-1 TALLYING ... REPLACING ...
INSPECT identifier-1 CONVERTING { id | lit } TO { id | lit } [ba]
where [ba] is: [ BEFORE INITIAL { id | lit } ] [ AFTER INITIAL { id | lit } ]
The rules of the Language Reference that matter most:
-
TALLYINGadds to the count field, which must be an integer item that you initialise;INSPECTnever resets it.CHARACTERScounts every position (spaces included),ALLcounts each nonoverlapping occurrence of a string, andLEADINGcounts occurrences only while they are unbroken from the start of the scanned region. -
REPLACINGsubstitutes the characters. The subject and substitution strings must have the same length, andCHARACTERS BYneeds a one-character substitution.ALLreplaces every nonoverlapping occurrence,LEADINGonly the unbroken run at the start, andFIRSTonly the leftmost one. Once a position is replaced it is not examined again in the same statement. -
CONVERTING x TO yis a table of single-character replacements: the characters ofxare translated to the characters at the same positions ofy, which must be the same length. The same character must not appear twice inx. -
BEFORE INITIALandAFTER INITIALrestrict the area scanned:BEFOREstops at the first occurrence of the delimiter (or scans to the end if there is none),AFTERstarts just past the first occurrence (and does nothing if there is none). The delimiters themselves are not counted or replaced. At most oneBEFOREand oneAFTERper phrase. -
At each position the comparands are tried in the order written and the first match wins. When
TALLYINGandREPLACINGare combined the statement works as the tally followed by the replacement. -
Operands must be
DISPLAY,DISPLAY-1orNATIONALof one and the same usage. A numericDISPLAYitem is examined as the characters of its digits (a signed one as an unsigned copy, with a separate sign byte left alone), soINSPECTcan edit a numeric field, but the result is only meaningful if it still holds digits.
IDENTIFICATION DIVISION.
PROGRAM-ID. INSP1.
* INSPECT: TALLYING, REPLACING, CONVERTING, BEFORE and AFTER.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TEXT PIC X(30).
01 WS-N PIC 9(3).
01 WS-M PIC 9(3).
01 WS-ISBN PIC X(13) VALUE "978-0-00-0001".
01 WS-AMT PIC X(8) VALUE "00001250".
PROCEDURE DIVISION.
MAIN-PARA.
MOVE "the old ledgers, old books" TO WS-TEXT
* TALLYING: ALL, LEADING, CHARACTERS.
MOVE ZERO TO WS-N
INSPECT WS-TEXT TALLYING WS-N FOR ALL "old"
DISPLAY "ALL old : " WS-N
MOVE ZERO TO WS-N
INSPECT WS-TEXT TALLYING WS-N FOR ALL SPACE
DISPLAY "ALL SPACE : " WS-N
MOVE ZERO TO WS-N
INSPECT WS-TEXT TALLYING WS-N FOR CHARACTERS
BEFORE INITIAL ","
DISPLAY "CHARS BEFORE , : " WS-N
MOVE ZERO TO WS-N
INSPECT WS-AMT TALLYING WS-N FOR LEADING "0"
DISPLAY "LEADING 0 : " WS-N
* One statement, two counters.
MOVE ZERO TO WS-N WS-M
INSPECT WS-TEXT TALLYING WS-N FOR ALL "o"
WS-M FOR ALL "k"
DISPLAY "o and k : " WS-N " " WS-M
* REPLACING: ALL, FIRST, LEADING, CHARACTERS.
INSPECT WS-TEXT REPLACING FIRST "old" BY "new"
DISPLAY "FIRST : [" WS-TEXT "]"
INSPECT WS-TEXT REPLACING ALL "old" BY "new"
DISPLAY "ALL : [" WS-TEXT "]"
INSPECT WS-AMT REPLACING LEADING "0" BY SPACE
DISPLAY "LEADING : [" WS-AMT "]"
INSPECT WS-ISBN REPLACING ALL "-" BY SPACE
DISPLAY "ISBN no hyphen : [" WS-ISBN "]"
* BEFORE / AFTER limit the scan.
MOVE "a-b-c-d" TO WS-TEXT
INSPECT WS-TEXT REPLACING ALL "-" BY "+"
AFTER INITIAL "b"
DISPLAY "AFTER b : [" WS-TEXT "]"
MOVE "a-b-c-d" TO WS-TEXT
INSPECT WS-TEXT REPLACING ALL "-" BY "+"
BEFORE INITIAL "c"
DISPLAY "BEFORE c : [" WS-TEXT "]"
* CONVERTING = a table of single-character replacements.
MOVE "Dr. grace hopper" TO WS-TEXT
INSPECT WS-TEXT CONVERTING
"abcdefghijklmnopqrstuvwxyz"
TO "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
DISPLAY "CONVERTING : [" WS-TEXT "]"
* TALLYING and REPLACING in one statement.
MOVE "x,y,z" TO WS-TEXT
MOVE ZERO TO WS-N
INSPECT WS-TEXT TALLYING WS-N FOR ALL ","
REPLACING ALL "," BY ";"
DISPLAY "both : " WS-N " [" WS-TEXT "]"
* The counter is added to, never reset by INSPECT.
MOVE ZERO TO WS-N
INSPECT WS-TEXT TALLYING WS-N FOR ALL ";"
INSPECT WS-TEXT TALLYING WS-N FOR ALL "x"
DISPLAY "accumulated : " WS-N
* The first matching comparand wins at each position.
MOVE "ABC" TO WS-TEXT
MOVE ZERO TO WS-N
INSPECT WS-TEXT TALLYING WS-N FOR ALL "AB" ALL "BC"
DISPLAY "AB then BC : " WS-N
STOP RUN.
ALL old : 002
ALL SPACE : 008
CHARS BEFORE , : 015
LEADING 0 : 004
o and k : 004 001
FIRST : [the new ledgers, old books ]
ALL : [the new ledgers, new books ]
LEADING : [ 1250]
ISBN no hyphen : [978 0 00 0001]
AFTER b : [a-b+c+d ]
BEFORE c : [a+b+c-d ]
CONVERTING : [DR. GRACE HOPPER ]
both : 002 [x;y;z ]
accumulated : 003
AB then BC : 001
A few results deserve a second look:
-
ALL SPACEcounted 8, not 4: the item is 30 characters long and the text fills 26, so the four trailing pad spaces are counted with the four between words.CHARACTERS BEFORE INITIAL ","gave 15, the characters ofthe old ledgers. -
LEADING "0"on00001250counted 4 andREPLACING LEADING "0" BY SPACEproduced ` 1250`: a quick way to suppress zeros in a character field, although an edited picture (PIC Z(7)9) is the normal tool for numbers. -
Two
INSPECTstatements on the same counter accumulate (2 plus 1 gave 3). In"AB" ... "BC"overABConly the first comparand counted: onceABmatched, the scan moved pastB, andBCcould no longer start there. -
The
BEFOREandAFTERphrases changed only part ofa-b-c-d.CONVERTINGupper-cased the whole name by translating 26 letters to 26 letters.FUNCTION UPPER-CASEdoes the same for plain text and is shorter, butCONVERTINGcan express any translation table, such as mapping accented letters to plain ones.
Operands of different sizes are rejected at compile time:
IDENTIFICATION DIVISION.
PROGRAM-ID. BAD1.
* Deliberately wrong: operands of different sizes.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TEXT PIC X(30) VALUE "the old ledgers".
PROCEDURE DIVISION.
MAIN-PARA.
INSPECT WS-TEXT REPLACING ALL "old" BY "ne"
INSPECT WS-TEXT CONVERTING "abc" TO "AB"
STOP RUN.
bad1.cob:9: error: REPLACING operands differ in size
bad1.cob:10: error: CONVERTING operands differ in size
GnuCOBOL also accepts a TRAILING phrase in INSPECT TALLYING and REPLACING (INSPECT x TALLYING n FOR TRAILING SPACE
gave 11 for the 9-character AA/B/C//D in a PIC X(20) item). It is not in the Enterprise COBOL 6.5 INSPECT formats,
which offer only ALL, LEADING and FIRST. Where portability to z/OS matters, count the trailing spaces with
FUNCTION LENGTH and FUNCTION TRIM instead.
INSPECT BACKWARD (COBOL 2023)
The COBOL 2023 standard adds a BACKWARD phrase to INSPECT, so that the scan runs from the right end of the item to the left.
The use is to replace or count the last occurrence of something, for example the last / of a path, which the forward-only
FIRST cannot do. Neither compiler in this section implements it:
-
Enterprise COBOL for z/OS 6.5 has no
BACKWARDphrase in any of itsINSPECTformats (the word does not appear in the Language Reference), so the same effect needs a reference-modification loop. -
GnuCOBOL 3.2.0 lists
BACKWARDas a reserved word but does not implement the phrase. In the default dialect the next program is a compile error, and the same under-std=ibm. Only under-std=mfdoes the parser acceptINSPECT BACKWARD item, placed right afterINSPECT, and then it warns that the phrase is not implemented and the statement runs forward.
IDENTIFICATION DIVISION.
PROGRAM-ID. INSP2.
* INSPECT ... BACKWARD (COBOL 2023) in GnuCOBOL 3.2.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PATH PIC X(20) VALUE "A/B/C/D".
01 WS-N PIC 9(3).
PROCEDURE DIVISION.
MOVE ZERO TO WS-N
INSPECT WS-PATH TALLYING WS-N FOR ALL "/"
DISPLAY WS-N
INSPECT BACKWARD WS-PATH REPLACING FIRST "/" BY "|"
DISPLAY "[" WS-PATH "]"
STOP RUN.
Default dialect (the same two errors appear with -std=ibm):
insp2.cob:12: error: 'BACKWARD' cannot be used here
insp2.cob:12: error: syntax error, unexpected Identifier, expecting CONVERTING or REPLACING or TALLYING
With -std=mf:
insp2.cob:13: warning: INSPECT BACKWARD is not implemented [-Wpending]
Source: GnuCOBOL 3.2 Manual
With -std=mf the replacement FIRST "/" BY "|" hit the first slash of A/B/C/D and printed A|B/C/D, where a real
backward scan would give A/B/C|D. Until your compilers support the phrase, write the backward search by hand with reference
modification, as in this program, which scans from the last non-space character towards the start:
IDENTIFICATION DIVISION.
PROGRAM-ID. BACKWD.
* The last "/" of a path, found with reference modification.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PATH PIC X(20) VALUE "A/B/C/D".
01 WS-I PIC 9(3).
PROCEDURE DIVISION.
MAIN-PARA.
MOVE FUNCTION LENGTH(FUNCTION TRIM(WS-PATH TRAILING))
TO WS-I
PERFORM UNTIL WS-I = 0
IF WS-PATH(WS-I:1) = "/"
MOVE "|" TO WS-PATH(WS-I:1)
EXIT PERFORM
END-IF
SUBTRACT 1 FROM WS-I
END-PERFORM
DISPLAY "[" WS-PATH "] last slash was at " WS-I
STOP RUN.
[A/B/C|D ] last slash was at 006
FUNCTION REVERSE is another common substitute: reverse the item, INSPECT ... FIRST, and reverse it back. The text of the
standard is paywalled, so this page does not quote its wording of the phrase; see the ISO catalogue entry in the references.
Text intrinsic functions
Intrinsic functions return a value that is used where an item of that type could be. The text ones are the pieces that
STRING, UNSTRING and INSPECT do not cover. The notation, FUNCTION name(arguments), and the whole function
catalogue are on Intrinsic Functions.
| Function | Result | Where available |
|---|---|---|
|
|
Enterprise COBOL 6.5, GnuCOBOL 3.2 |
|
The text with letters converted to upper or lower case; other characters are unchanged. |
Enterprise COBOL 6.5, GnuCOBOL 3.2 |
|
The characters in reverse order. |
Enterprise COBOL 6.5, GnuCOBOL 3.2 |
|
The arguments joined as they are, trailing spaces included. |
GnuCOBOL 3.2 only (not in Enterprise COBOL 6.5) |
|
|
GnuCOBOL 3.2 only (not in Enterprise COBOL 6.5) |
|
Characters and bytes of an item, group or function result. |
Enterprise COBOL 6.5, GnuCOBOL 3.2 |
|
The number held in the text |
Enterprise COBOL 6.5, GnuCOBOL 3.2 |
The first program applies the portable functions to a member name with leading spaces and mixed case. The function result can
be sent to a DISPLAY, moved to an item, reference-modified, passed to STRING, or nested in another function.
IDENTIFICATION DIVISION.
PROGRAM-ID. FUNC1.
* Intrinsic functions for text, applied to Circulation data.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY MEMBREC.
01 WS-OUT PIC X(60).
01 WS-BLANK PIC X(5) VALUE SPACES.
PROCEDURE DIVISION.
MAIN-PARA.
MOVE " grace HOPPER" TO MEMBER-NAME
DISPLAY "TRIM [" FUNCTION TRIM(MEMBER-NAME) "]"
DISPLAY "TRIM LEAD [" FUNCTION TRIM(MEMBER-NAME LEADING)
"]"
DISPLAY "TRIM TRAIL [" FUNCTION TRIM(MEMBER-NAME TRAILING)
"]"
DISPLAY "UPPER-CASE ["
FUNCTION UPPER-CASE(FUNCTION TRIM(MEMBER-NAME)) "]"
DISPLAY "LOWER-CASE ["
FUNCTION LOWER-CASE(FUNCTION TRIM(MEMBER-NAME)) "]"
DISPLAY "REVERSE ["
FUNCTION REVERSE(FUNCTION TRIM(MEMBER-NAME)) "]"
DISPLAY "LENGTH "
FUNCTION LENGTH(FUNCTION TRIM(MEMBER-NAME))
DISPLAY "LENGTH blank "
FUNCTION LENGTH(FUNCTION TRIM(WS-BLANK))
* Receiving the result with MOVE and STRING.
MOVE FUNCTION UPPER-CASE(MEMBER-NAME(4:5)) TO WS-OUT
DISPLAY "MOVE [" FUNCTION TRIM(WS-OUT) "]"
MOVE SPACES TO WS-OUT
STRING FUNCTION TRIM(MEMBER-NAME) DELIMITED BY SIZE
", member" DELIMITED BY SIZE
INTO WS-OUT
END-STRING
DISPLAY "STRING [" FUNCTION TRIM(WS-OUT) "]"
STOP RUN.
TRIM [grace HOPPER]
TRIM LEAD [grace HOPPER ]
TRIM TRAIL [ grace HOPPER]
UPPER-CASE [GRACE HOPPER]
LOWER-CASE [grace hopper]
REVERSE [REPPOH ecarg]
LENGTH 000000012
LENGTH blank 000000000
MOVE [GRACE]
STRING [grace HOPPER, member]
Most of what people do with TRIM is in front of a STRING or a DISPLAY: STRING FUNCTION TRIM(MEMBER-NAME) DELIMITED BY SIZE
does what DELIMITED BY " " did above without needing two spaces to be present. LENGTH of a trimmed item is the
number of characters actually in use. For an all-space argument TRIM gives a zero-length result: the length printed as
000000000, which is also what the Enterprise COBOL 6.5 manual documents.
The second program uses the GnuCOBOL-only functions. CONCAT joins its arguments without removing any padding, so give it
trimmed arguments when the items have trailing spaces; SUBSTITUTE takes any number of find/replace pairs.
IDENTIFICATION DIVISION.
PROGRAM-ID. FUNC2.
* GnuCOBOL extensions: CONCAT and SUBSTITUTE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ISBN PIC X(13) VALUE "978-0-00-0001".
01 WS-LINE PIC X(30)
VALUE "Return of the old books".
PROCEDURE DIVISION.
MAIN-PARA.
DISPLAY "CONCAT ["
FUNCTION CONCAT("ISBN:", WS-ISBN) "]"
DISPLAY "SUBSTITUTE ["
FUNCTION SUBSTITUTE(WS-ISBN, "-", "/") "]"
DISPLAY "SUBST many ["
FUNCTION TRIM(FUNCTION SUBSTITUTE(WS-LINE,
"old", "new", "Return", "Checkout")) "]"
STOP RUN.
CONCAT [ISBN:978-0-00-0001]
SUBSTITUTE [978/0/00/0001]
SUBST many [Checkout of the new books]
Source: GnuCOBOL 3.2 Manual
Enterprise COBOL 6.5 does not have these two (neither appears in the Language Reference), so code that has to run on z/OS builds the
result with STRING and INSPECT REPLACING or INSPECT CONVERTING. GnuCOBOL also compiles them under -std=ibm, so the
dialect option does not warn you. A replacement string of zero length looks like a way to delete characters, but it is not:
replacing - by "" as in FUNCTION SUBSTITUTE(WS-ISBN, "-", "") compiled with this warning and the
empty literal became a single space.
subs_bad.cob:14: warning: alphanumeric literal has zero length; a SPACE will be assumed [-Wothers]
Source: GnuCOBOL 3.2 Manual
To delete characters, use a nested FUNCTION TRIM, a STRING that skips them, or
UNSTRING ... DELIMITED BY "-" followed by a STRING of the pieces.
Parsing a CSV line
Files from non-mainframe systems often arrive as comma separated values (CSV): one record per line, fields separated by commas, text
in double quotes when it contains a comma or a quote, a header line, and CR LF or LF line ends. COBOL has no CSV reader,
so the program does it with UNSTRING for simple lines and a character scanner when quoted fields can contain commas.
The next program loads the new books of a file books.csv. The first line is a header, which it skips. Each of the other lines
is split with a single UNSTRING into five alphanumeric fields. COUNT IN measures the ISBN and the title as delivered, and
TALLYING IN counts the fields. A line is accepted when it has exactly five fields, a 13-character ISBN and a title
that fits the 30 characters of BOOK-TITLE. The numeric text is converted with FUNCTION NUMVAL, which tolerates leading zeros, a
sign and a decimal point, so 15.50 reaches BOOK-PRICE (PIC 9(4)V99) correctly. The data file, written by a PC tool, has
a CR LF line end on its third line, then one line with too many fields and one with too few:
id,isbn,title,copies,price
5,9780000000059,TAPE LIBRARIES,2,15.50
6,9780000000066,JOB SCHEDULING,1,22.00
7,9780000000073,TOO,MANY,FIELDS,9,1.00
8,9780000000080,SHORT ROW,3
IDENTIFICATION DIVISION.
PROGRAM-ID. CSV1.
* Load BOOKREC records from a CSV exported by a PC tool.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CSV-FILE ASSIGN TO "books.csv"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD CSV-FILE.
01 CSV-LINE PIC X(100).
WORKING-STORAGE SECTION.
COPY BOOKREC.
01 WS-EOF PIC X VALUE "N".
88 AT-END VALUE "Y".
01 WS-LINE-NO PIC 9(3) VALUE ZERO.
01 WS-FIELDS.
05 WS-ID-X PIC X(10).
05 WS-ISBN-X PIC X(20).
05 WS-TITLE-X PIC X(40).
05 WS-COPIES-X PIC X(10).
05 WS-PRICE-X PIC X(12).
01 WS-COUNTS.
05 WS-TITLE-LEN PIC 9(3).
05 WS-ISBN-LEN PIC 9(3).
01 WS-NFIELDS PIC 9(2).
01 WS-OK PIC 9(2) VALUE ZERO.
01 WS-BAD PIC 9(2) VALUE ZERO.
PROCEDURE DIVISION.
MAIN-PARA.
OPEN INPUT CSV-FILE
READ CSV-FILE
AT END SET AT-END TO TRUE
END-READ
* The first line is the header: skip it.
PERFORM UNTIL AT-END
READ CSV-FILE
AT END SET AT-END TO TRUE
NOT AT END
ADD 1 TO WS-LINE-NO
PERFORM PARSE-LINE
END-READ
END-PERFORM
CLOSE CSV-FILE
DISPLAY "loaded " WS-OK ", rejected " WS-BAD
STOP RUN.
PARSE-LINE.
MOVE ZERO TO WS-NFIELDS WS-TITLE-LEN WS-ISBN-LEN
MOVE SPACES TO WS-FIELDS
UNSTRING CSV-LINE DELIMITED BY ","
INTO WS-ID-X
WS-ISBN-X COUNT IN WS-ISBN-LEN
WS-TITLE-X COUNT IN WS-TITLE-LEN
WS-COPIES-X
WS-PRICE-X
TALLYING IN WS-NFIELDS
ON OVERFLOW
PERFORM REJECT-LINE
NOT ON OVERFLOW
PERFORM CHECK-AND-STORE
END-UNSTRING.
CHECK-AND-STORE.
IF WS-NFIELDS NOT = 5 OR WS-ISBN-LEN NOT = 13
OR WS-TITLE-LEN > 30
PERFORM REJECT-LINE
ELSE
MOVE FUNCTION NUMVAL(WS-ID-X) TO BOOK-ID
MOVE WS-ISBN-X TO BOOK-ISBN
MOVE WS-TITLE-X TO BOOK-TITLE
MOVE FUNCTION NUMVAL(WS-COPIES-X) TO BOOK-COPIES
MOVE FUNCTION NUMVAL(WS-PRICE-X) TO BOOK-PRICE
SET BOOK-ACTIVE TO TRUE
ADD 1 TO WS-OK
DISPLAY "ok " WS-LINE-NO " " BOOK-ID " " BOOK-TITLE
" " BOOK-COPIES " " BOOK-PRICE
END-IF.
REJECT-LINE.
ADD 1 TO WS-BAD
DISPLAY "bad " WS-LINE-NO " fields " WS-NFIELDS.
ok 001 000005 TAPE LIBRARIES 002 0015.50
ok 002 000006 JOB SCHEDULING 001 0022.00
bad 003 fields 05
bad 004 fields 04
loaded 02, rejected 02
Points to notice:
-
The long line has seven pieces for five receivers, so
ON OVERFLOWrejects it. The short line has four pieces, theUNSTRINGends normally with a tally of 4, and the explicit tally check rejects it. Both checks are needed. -
In GnuCOBOL 3.2.0
READof aLINE SEQUENTIALfile removes the carriage return of aCR LFline end (a test withINSPECT ... TALLYING FOR ALL X"0D"counted none on line 3), which is why no cleaning step is needed. Do not assume the same elsewhere: a file transferred to z/OS in binary mode, or read as fixed-length records, keeps itsX"0D"as the last character of the last field, andNUMVALor a comparison can then fail. Convert the line ends (or the code page, see National Data, UTF-8 and Code Pages) when the file is uploaded, orINSPECT ... REPLACING ALL X"0D" BY SPACEbefore parsing. Enterprise COBOL supportsLINE SEQUENTIALonly for z/OS UNIX files. -
The 13-character check on
COUNT INis what catches an ISBN that was cut by thePIC X(20)receiver, or too short. -
UNSTRINGcannot tell a comma that separates fields from one inside quotes. The title"Ledgers, Old and New"would be split in two. The next program shows a scanner built on reference modification that handles quoted fields and the doubled-quote escape"", one character at a time.
IDENTIFICATION DIVISION.
PROGRAM-ID. CSV2.
* Split a CSV line that has quoted fields (UNSTRING cannot).
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SAMPLES.
05 FILLER PIC X(60) VALUE
'9,"Ledgers, Old and New",2'.
05 FILLER PIC X(60) VALUE
'10,"The ""Batch"" Handbook",1'.
05 FILLER PIC X(60) VALUE
'11,Plain Title,,4'.
01 WS-SAMPLE-TABLE REDEFINES WS-SAMPLES.
05 WS-SAMPLE PIC X(60) OCCURS 3 TIMES.
01 WS-IN PIC X(60).
01 WS-LEN PIC 9(3).
01 WS-I PIC 9(3).
01 WS-N PIC 9(2).
01 WS-K PIC 9(2).
01 WS-OUT-LEN PIC 9(3).
01 WS-CH PIC X.
88 CH-QUOTE VALUE '"'.
88 CH-COMMA VALUE ",".
01 WS-NEXT PIC X.
88 NEXT-QUOTE VALUE '"'.
01 WS-QUOTED PIC X.
88 IN-QUOTES VALUE "Y".
88 NOT-IN-QUOTES VALUE "N".
01 WS-SKIP PIC X.
88 SKIP-NEXT VALUE "Y".
88 NO-SKIP VALUE "N".
01 WS-FIELD-TABLE.
05 WS-FIELD PIC X(30) OCCURS 6 TIMES.
PROCEDURE DIVISION.
MAIN-PARA.
PERFORM VARYING WS-K FROM 1 BY 1 UNTIL WS-K > 3
MOVE WS-SAMPLE(WS-K) TO WS-IN
PERFORM SPLIT-LINE
DISPLAY "line " WS-K ": " WS-N " fields"
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > WS-N
DISPLAY " " WS-I " [" FUNCTION TRIM(WS-FIELD(WS-I))
"]"
END-PERFORM
END-PERFORM
STOP RUN.
SPLIT-LINE.
MOVE SPACES TO WS-FIELD-TABLE
MOVE FUNCTION LENGTH(FUNCTION TRIM(WS-IN TRAILING))
TO WS-LEN
MOVE 1 TO WS-N
MOVE ZERO TO WS-OUT-LEN
SET NOT-IN-QUOTES TO TRUE
SET NO-SKIP TO TRUE
PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > WS-LEN
IF SKIP-NEXT
SET NO-SKIP TO TRUE
ELSE
MOVE WS-IN(WS-I:1) TO WS-CH
MOVE SPACE TO WS-NEXT
IF WS-I < WS-LEN
MOVE WS-IN(WS-I + 1:1) TO WS-NEXT
END-IF
EVALUATE TRUE
WHEN CH-QUOTE AND IN-QUOTES AND NEXT-QUOTE
PERFORM APPEND-CHAR
SET SKIP-NEXT TO TRUE
WHEN CH-QUOTE AND IN-QUOTES
SET NOT-IN-QUOTES TO TRUE
WHEN CH-QUOTE
SET IN-QUOTES TO TRUE
WHEN CH-COMMA AND NOT-IN-QUOTES
ADD 1 TO WS-N
MOVE ZERO TO WS-OUT-LEN
WHEN OTHER
PERFORM APPEND-CHAR
END-EVALUATE
END-IF
END-PERFORM.
APPEND-CHAR.
ADD 1 TO WS-OUT-LEN
MOVE WS-CH TO WS-FIELD(WS-N)(WS-OUT-LEN:1).
line 01: 03 fields
001 [9]
002 [Ledgers, Old and New]
003 [2]
line 02: 03 fields
001 [10]
002 [The "Batch" Handbook]
003 [1]
line 03: 04 fields
001 [11]
002 [Plain Title]
003 []
004 [4]
The scanner keeps one state, whether it is inside quotes, and one output position. A comma outside quotes starts the next
field, a quote toggles the state, and two quotes inside quotes append one quote character and skip the second. Appending
uses a reference-modified, subscripted target WS-FIELD(WS-N)(WS-OUT-LEN:1). The empty third field of line 3 is simply a field
of spaces. A quote that never closes would swallow the rest of the line, which a production loader should report. Condition names
(CH-QUOTE, NEXT-QUOTE) keep the EVALUATE lines within the 72-column limit of fixed reference format; a longer line is silently cut at column 72 (see
Reference Format: Fixed and Free), which turns the end of a condition into
a compile error such as continuation character expected.
The scanner can be moved into a called subprogram (see Subprograms: CALL and Linkage) so that every loader program shares it.
Differences between the standard, Enterprise COBOL and GnuCOBOL
| Topic | ISO/IEC 1989:2023 | Enterprise COBOL 6.5 | GnuCOBOL 3.2 |
|---|---|---|---|
|
Yes |
Yes, formats as on this page |
Yes; behaviour in the tests above matches the IBM rules |
|
Added in COBOL 2023 (as listed in the INCITS summary of changes) |
Not in the |
Reserved word only; syntax error by default, parsed but "not implemented" under |
|
Not verified (the standard is paywalled) |
Not available ( |
Accepted |
|
Not verified (the standard is paywalled) |
Integer without |
Accepted without a diagnostic, including non-integer and signed items |
Reference modification |
Yes |
Yes; start and length must be positive and in range, checked at run time with |
Yes; constants checked at compile time, variables only with |
Dynamic-length items and |
COBOL 2014 dynamic-length items |
6.3 and later: |
Not available (see the pointers page) |
UTF-8 ( |
Not verified (the standard is paywalled) |
6.3 and later: |
Experimental; see the national page |
|
Yes |
Yes |
Yes |
|
Not verified (the standard is paywalled) |
No |
Yes |
Where the book is outdated
Tom Taulli’s Modern Mainframe Development covers STRING, UNSTRING, INSPECT, reference modification, NUMVAL,
NUMVAL-C and the case functions in its chapter 6. Those statements have not changed in a way that makes
the basic forms wrong. What is out of date or missing is the surroundings. The book is written against Enterprise COBOL V6.3
as the newest release, so it cannot mention the 6.4 and 6.5 releases or the COBOL 2023 INSPECT BACKWARD phrase, which no shipping compiler implements yet (see above). Its examples were
written for GnuCOBOL through OpenCobolIDE, which is no longer maintained; use GnuCOBOL 3.2 with the diagnostics shown on this page.
The remarks on this page about padding, truncation, unset pointers and numeric senders are the situations where
a short example works and a real file does not, so test with the lengths and line ends of your real data.