Cobol Programming For Business Success
Cobol Programming For Business Success
Business Success
1 14/06/2016 175
Prerequisites
To get the maximum value from this course, you should meet the following
prerequisites:
• Basic Windows familiarity
• Some familiarity with the concept of programming
• IMPORTANT! Have the class files.
Audience
The audience for this course includes programming project managers, programmers,
and analysts.
Objectives
Upon successful completion of this course, you will be able to:
• Describe the advantages of programming with COBOL.
• Include the divisions in the correct order within a COBOL program.
• Use verbs in the PROCEDURE DIVISION.
• Include meaningful comments when writing code.
• Design a typical COBOL file-handling program.
• Design and code a typical COBOL program which handles sequential files.
• Write a COBOL program using different types of condition testing.
• Use the various arithmetic COBOL verbs.
• Use the verbs for manipulating character data.
• Describe the role of reference modification.
• Use subscripts and indexes to manipulate tables.
• Write fully functioning print programs.
• Describe the uses of indexed files.
Compared with other programming languages, COBOL results in programs that are
verbose. This dramatically differs with languages such as C, where programs can be
more obscure. COBOL, on the other hand, encourages coding that is easy to read and,
therefore, simple to maintain. Because modern COBOL compilers produce tight,
efficient code, wordiness in a program does not reduce performance. COBOL is one of
the most readable and self-documenting programming languages in use today.
COBOL was developed by Rear Admiral Grace Hopper in 1959 as a language that would
be relevant for commercial implementation. It has a long and steady history with
continued developments that make it a viable choice for many applications.
In the past, mainframes processed large quantities of data. Since then, many
companies consider client/server technology for their processing needs. COBOL
supports this technology with its basic business logic remaining sound.
Although COBOL has been enhanced to meet today's computing needs, it remains true
to strict standards as developed by the American National Standards Institute (ANSI).
Both mainframes and desktop computers support COBOL programs. COBOL also
provides database file access, providing another upgrade to its original language
standard.
COBOL can be viewed as the internal combustion engine of the computing community:
it is familiar, perceived as unglamorous, and largely taken for granted. Just like the
internal combustion engine, it is, and will continue to be, a vital part of many of our
NOTE: An appendix to this course provides brief introductions to two types of editors,
which you can use to write your programs.
A compiler processes the statements you created in the source file into information
that the computer can execute. The compiler also identifies syntax errors, which are
the equivalent of spelling mistakes in a document. However, logic errors (the
equivalent of using totally the wrong words in a document) pass unnoticed. The
programmer must find and correct these. (Ideally, programs should omit logic errors.)
A COBOL program includes four divisions. Although some compilers will permit the
omission of some of the divisions, they must appear in the following sequence.
• IDENTIFICATION DIVISION – the first division. Insert statements here that
define the name of the program. Add comments that describe the program's
function.
• ENVIRONMENT DIVISION – the second division. Here, define the particular
system environment under which the program will run.
• DATA DIVISION – the third division. This is where all data is defined. All
programs manipulate data in some way.
• PROCEDURE DIVISION – the final division. Write in this division all the
statements that determine what the program does. Any data items referred to
here must have been already defined in the Data Division.
The following is a stub of a COBOL program. Look for the four divisions; don't worry
about the specifics of the code.
COBOL Program
Identification Division
Environment Division
Configuration Section
Input-Output Section
Data Division
File Section
Working-Storage Section
Linkage Section
Communication Section
Report Section
Screen Section
Procedure Division
[can have sections, but must have paragraphs]
Look at the sample on the following page and locate the sections within the divisions.
The word "section" appears in its label.
Some sections include paragraphs, which contain multiple statements. Within the
INPUT-OUTPUT SECTION in the following code, a paragraph begins with a paragraph
heading, similar to what you see in this book.
Open x:\class\module1\netx\example1.cbl
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE1.
AUTHOR. TRAINING.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "DATA-IN.DAT".
SELECT OUTFILE ASSIGN "FILE-OUT.DAT".
DATA DIVISION.
FILE SECTION.
FD INFILE.
01 INFILE-REC PIC X(80).
FD OUTFILE.
01 OUTFILE-REC PIC X(132).
WORKING-STORAGE SECTION.
01 WORK-FIELD PIC X(20).
LINKAGE SECTION.
01 LS-FIELD PIC X(30).
PROCEDURE DIVISION.
MAINLINE SECTION.
DISPLAY "HELLO WORLD".
GOBACK.
The FILE-CONTROL paragraph describes how the program uses files specified in the
program. In this case, the files are called INFILE and OUTFILE.
Lastly, programs include statements and many of them. We could put the following
statements on two lines to identify each statement more easily.
FILE-CONTROL. [Paragraph heading]
SELECT INFILE [Statement]
ASSIGN "DATA-IN.DAT". [Statement]
Mainframe
FILE-CONTROL. [Paragraph heading]
SELECT INFILE [Statement]
ASSIGN UT-INFILE [Statement]
1.5. Summary
In this module we have examined the following topics:
• The history and purpose of COBOL as a programming language.
• The structure of a COBOL program.
This module explores the divisions in more detail and analyzes statements written in
each section. Because divisions and statements must be placed in a specific location,
we must look at COBOL programming layout.
....+....1....+....2....+....3....+....4....+....5..
PROCEDURE DIVISION.
000-MAIN SECTION.
Notice that the "P" in Procedure Division appears starting at the 8th column. Each 72-
Some columns are reserved for specific items in categories called areas, as listed in the
following graphic.
These layout rules are referred to as the COBOL reference format. Let's inspect each
area.
Columns 1–6 are not typically used. They used to be reserved for line numbering,
referred to as sequence numbering. Line numbers can help identify each line in a
program. If you use sequence numbers, they must be six digits. In the past only the
compiler used these columns. So anything here is ignored. This means that if a COBOL
statement begins, say in column 1, the first six characters of the statement will be
ignored. This will probably reduce the rest of the statement to nonsense.
Indicator Area
Column 7 is a special column, where only certain characters can be inserted. By far the
most common of these is an asterisk ('*'). Placing an asterisk here tells the compiler to
see the whole line as a comment. Because COBOL programs may have a very long life,
insert comments into your code whenever possible (anywhere in the program after the
IDENTIFICATION DIVISION). Comments in the code help ensure that at least some
documentation will be available for years to come.
....+....1....+....2....+....3....+....4....+....5..
* *** THIS IS A COMMENT ***
Area A
Columns 8-11 are known as Area A. All Division names, Section names, and paragraph
headings must start here. Also, all 01 data items, which are discussed later, must start
here. Statements under the headings appear indented, making programs easier to
read.
Additionally, all Division and Section headings must appear on one line without other
entries and must end with a period.
Paragraph headings must begin in Area A, but can appear on a line with or without
other entries. Although some paragraph headings do not require periods, adding
periods after paragraph headings will never create errors. So, adding the period might
be easier to remember.
Area B
Columns 12-72 are known as Area B. The main body of the program appears here. Also,
all entries that began in Area A, or comments, can continue here.
Similar to Area B, statements can begin anywhere starting position 12 through 72, but
for ease of reading, programmers typically start Area B statements at position 12 and
indent related statements.
Statements in Area B end with a period if the statement ends a paragraph or a section.
Identifying Areas
The following code places all the divisions in their correct column layout. The bolded
statements begin in Area A at position 8. All other statements appear in Area B starting
at position 12.
....+....1....+....2....+....3....+....4....+....5..
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE2.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN TO "EMPL.DAT".
SELECT OUTFILE ASSIGN TO "EMPLOUT.DAT".
The IDENTIFICATION division, which is the first and simplest division, does not include
any sections. The IDENTIFICATION SECTION names the program. It can have only one
entry: the PROGRAM-ID.
....+....1....+....2....+....3....+....4....+....5..
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE3.
PROGRAM-ID starts in column 8 (Area A). The program name often starts in column 40,
although this is not necessary.
Within the IDENTIFICATION DIVISION you can insert comment-entries with associated
values that provide details about the program, such as the author and date written.
The compiler ignores the values.
If using comment entries, include them in the following order and include periods.
....+....1....+....2....+....3....+....4....+....5..
IDENTIFICATION DIVISION
PROGRAM-ID. EXAMPLE3.
AUTHOR. SALLY ROGERS.
INSTALLATION. MARS.
DATE-WRITTEN. 18th SEPTEMBER.
DATE-COMPILED. 21st OCTOBER.
SECURITY. NONE.
These entries can be very useful for maintenance purposes. Documentation supporting
COBOL programs may become lost over the decades in which they are used. Include
important information directly in the program whenever possible.
• The FILE SECTION always appears when files are accessed in the program.
• The WORKING-STORAGE SECTION contains all data items that the program
needs, for example, counters of how many records have been written or data
resulting from intermediate calculations.
• A LINKAGE SECTION is needed when the program is being called from another
program. In this case data values (called "parameters") are probably being
passed back and forth. These are defined in this section.
This snippet from the previous code shows only the DATA DIVISION.
....+....1....+....2....+....3....+....4....+....5..
PROCEDURE DIVISION USING LS-FIELD.
000-MAIN SECTION. [Section
heading]
010-MAIN. [Paragraph heading]
OPEN INPUT INFILE, OUTPUT OUTFILE
PERFORM 110-READ-AND-WRITE 10 TIMES
CLOSE INFILE, OUTFILE
GOBACK
100-PROCESSING SECTION.
110-READ-AND-WRITE.
READ INFILE
AT END GOBACK
END-READ
IF INFILE-REC IS NOT EQUAL TO SPACES THEN
WRITE OUTFILE-REC FROM INFILE-REC
END-IF.
120-END.
EXIT.
NOTE: In most of remaining code samples, exact column layout positions are not
shown due to space limitations.
The rules for using periods in the PROCEDURE DIVISION are slightly different, as
discussed in a later module.
2.9. Summary
In this module we have examined the following topics:
In this exercise, create a skeleton COBOL program that includes the 4 divisions and the
sections in each division.
Details
2. Enter in the following information insuring that all typed information begins in column 8:
IDENTIFICATION DIVISION.
PROGRAM-ID. PROGRAM2.
AUTHOR. TRAINING.
INSTALLATION. MARS.
DATE-WRITTEN. 18/10/2005.
DATE-COMPILED.
SECURITY. MICROFOCUS
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
LINKAGE SECTION.
3. Save the program in the X: \CLASS\MODULE2\NETX directory and provide the program name
PROGRAM2.CBL.
Data is defined in the same way in all three sections within the DATA DIVISION.
Instructions using that data are provided in the PROCEDURE DIVISION. Learning about
data definitions applies to both the DATA and PROCEDURE DIVISIONS.
COBOL Program
Identification Division
Environment Division
Configuration Section
Input-Output Section
Data Division
File Section
Working-Storage Section
Linkage Section
Procedure Division
[can have sections, but must have paragraphs]
Data items are fields, for example, a last name or a telephone number. In COBOL, rules
govern how you can define data items. This module looks at how data items are
named, lists names that are reserved and cannot be used to identify your own fields,
and explains how data item characteristics can be defined using the PICTURE clause.
The use of long, meaningful names for data items is encouraged in COBOL, which
supports the fact that COBOL is an easy-to-read language.
The following table shows how you can provide COBOL names to data that used
currently.
Name data items with intuitive names. However, restrictions apply to names you can
define.
Reserved Words
COBOL uses some words for special processing instructions. These words are known as
reserved words. The data item name must not be a reserved word, such as DATA.
COBOL uses the word DATA for itself, so "DATA" cannot be used as a name. However,
you can use a reserved word as part of a larger name. "DATA-GROUP" would be
acceptable.
The following lists some reserved words that cannot be used when naming data fields.
• All Division and Section names and the words "DIVISION" and "SECTION"
• AND, CLOSE, DISPLAY, FILE, INPUT, MOVE, MULTIPLY, NOT, OPEN, OR, PICTURE,
RECORD, SPACE
Note: Online help of your editor provides a complete list of reserved words.
Invalid Names
Level numbers (for example, "01" or "03) preceding the data name represent the
hierarchy of the data. The following example shows the EMPLOYEE-NAME in the higher
Group Level with the three EMPLOYEE Elementary Level data fields indented below it.
We could have numbered the Elementary Level fields with "02" rather than "03." Using
a numbering scheme that skips numbers is highly recommended. Also, although
indenting is not required, it makes the program easier to read.
Actually EMPLOYEE-NAME is the name of a record that includes three data fields. A
record is group of associated fields.
The 01 level must be positioned in Area A (positions 8–11). The record's data fields
must be coded in Area B (positions 12–72) and be followed by a period.
....+....1....+....2....+....3....+....4....+....5..
01 EMPLOYEE-RECORD.
03 EMPLOYEE-NAME.
05 EMPLOYEE-TITLE PIC X(3).
05 EMPLOYEE-INITIALS PIC X(4).
05 EMPLOYEE-SURNAME PIC X(30).
03 EMPLOYEE-GENDER PIC X.
03 EMPLOYEE-ADDRESS.
There are many ways to define records; the definition depends on the results needed.
In the following example, someone has presumably decided that there is no need to
access the parts of the name individually. So, they have been defined as just one field.
....+....1....+....2....+....3....+....4....+....5..
01 EMPLOYEE-RECORD.
03 EMPLOYEE-PERSONAL-DETAILS.
05 EMPLOYEE-FULL-NAME PIC X(37).
05 EMPLOYEE-GENDER PIC X.
03 EMPLOYEE-ADDRESS.
PICTURE clauses are very closely related to the concepts of data hierarchy as PICTURE
clauses are used only with Elementary Level items (preceded below with "03").
Again, due to space constraints, the following code and much of the code for the
remainder of the manual is not placed in its true column.
The PICTURE clause provides information about the type of data stored and the size of
the storage area for the item.
Do not embed any spaces in PIC clause definitions. These are invalid: PIC X( 77) or PIC X
(77).
Use the PIC A syntax to identify a field as alphabetic. You could use format 1 or format
2.
Format 1
Format 2
In Format 2 each of the three As identifies the storage for one character. The 3 in the
first format is merely another way to write this. PIC A is rarely used.
Fields containing letters, numbers, and spaces are identified by PIC (X).
Alternatively, use the PIC 9(4) format, where the 4 represents the number of digits.
Always make the definitions for numeric fields large enough to accommodate any
possible number. If a field needs to hold 1 million, many programmers will at first
define this in a PIC 9(6) field. Such a fields will hold 999,999 with ease. However, adding
one sets the field to zero, as the leading '1' is lost to the left – probably not what is
needed.
Decimal Values
Each 9 after the V represents a digit after the decimal point. So, an EMPLOYEE-SALARY
would appear as $50000.00, two digits after the decimal point. Alternatively, you could
write this as shown next.
Negative Numbers
The notation of 9(5)V99 allows only for zero or positive values. However, if we needed
to allow for negative numbers, for example, for a customer's account balance, we
would say something like the following.
S stands for 'signed'. The sign is stored on the data item itself and does not take an
extra byte.
Literals
Invalid Literals
Once you define this in a COBOL program, you can access the information in the
following ways.
Because COBOL programs commonly read input records and write output records, this
sort of record hierarchy makes sense.
Using this example, we could write the following code in Working-Storage Section in
the DATA DIVISION.
..1....+....2....+....3....+....4....+....5....+
DATA DIVISION
FILE SECTION.
[more code]
WORKING-STORAGE SECTION.
01 EMPLOYEE-RECORD.
03 EMPLOYEE-NAME.
05 EMPLOYEE-TITLE PIC X(3)
05 EMPLOYEE-INITIALS PIC X(4)
05 EMPLOYEE-SURNAME PIC X(30)
03 EMPLOYEE-GENDER PIC X.
03 EMPLOYEE-ADDRESS.
05 EMPLOYEE-ADDRESS-1 PIC X(30)
05 EMPLOYEE-ADDRESS-2 PIC X(30)
05 EMPLOYEE-ADDRESS-3 PIC X(30)
05 EMPLOYEE-ADDRESS-4 PIC X(30)
05 EMPLOYEE-ZIP-POSTAL-CODE PIC X(8)
03 EMPLOYEE-SALARY PIC 9(5)V99.
Let's look at these entries one by one, and see what they mean.
Line 1
Firstly, EMPLOYEE-RECORD is the name given to the definition of the record as a whole.
Because it's at the top level, it has a level number of 01. It will appear in Area A of the
Data Division, meaning that the first character will be in column 8. This line is a Group
Level field, made up of lower-level items. Since it is not at the Elementary level, it does
not include a PIC clause.
Line 2
Rather than 03, we could have said 02 or 05, as long as the hierarchy logic is
maintained. Using odd-numbered levels allows us to insert extra levels later during
maintenance. In practice, however, this is rarely done. The programmer is more likely
to code the whole record again.
Lines 3 – 5
The X in PIC X indicates that any alphanumeric character can be used. To specify that
only alphabetic characters be used, we could have said
PIC A(3). However, PIC A is rarely used.
The next three lines show more Elementary Level items. Notice that PIC X is used for
EMPLOYEE-GENDER. PIC X(1) could be used instead.
Lines 7 –12
Line 13
Note first that this is a 03 level, even though it is also an Elementary level. This is
because it doesn't belong within the NAME or ADDRESS groups, so it is logically shown
as being one level below the record itself.
Its PICture is 9(5)V99, that is, a field capable of holding up to 99999, then a virtual
decimal point (hence the V), and two decimal places (the 99). The decimal point is
virtual in that it does not take up any space. If you looked at the field as it is stored, you
would see only seven digits even though the program "knows" to take the decimal
point into account.
01 EMPLOYEE-RECORD.
03 EMPLOYEE-NAME.
05 EMPLOYEE-TITLE PIC X(3).
05 EMPLOYEE-INITIALS PIC X(4).
05 EMPLOYEE-SURNAME PIC X(30).
03 EMPLOYEE-GENDER PIC X.
03 EMPLOYEE-ADDRESS.
05 EMPLOYEE-ADDRESS-1 PIC X(30).
05 EMPLOYEE-ADDRESS-2 PIC X(30).
05 EMPLOYEE-ADDRESS-3 PIC X(30).
05 EMPLOYEE-ADDRESS-4 PIC X(30).
05 EMPLOYEE-ZIP-POSTAL-CODE PIC X(8).
03 EMPLOYEE-SALARY PIC 9(5)V99.
• Can we access any field individually? Yes, because all Elementary fields (those
with a PIC clause defining its size) have individual names.
• Can we access the whole name as one unit? Yes, because it is a Group field,
called EMPLOYEE-NAME.
• Can we access the whole address as one unit? Yes, because it is named
EMPLOYEE-ADDRESS.
• Can we access the whole record as one unit? Yes, because it is the 01 level,
EMPLOYEE-RECORD itself.
However, as this is at byte 38 — the previous data fields were defined as PIC X(3), PIC
X(4), and PIC X(30) — the structure below is incorrect. This will be pointing at byte 1 of
the group field, not byte 38.
01 EMPLOYEE-RECORD.
03 EMPLOYEE-GENDER PIC X(01).
01 EMPLOYEE-RECORD.
03 FILLER PIC X(37).
03 EMPLOYEE-GENDER PIC X(01).
03 FILLER PIC X(135).
Use a FILLER clause to show that data is present, but you have no interest in accessing
it. FILLER clauses can be found in almost every program.
Instead of saying FILLER, you can omit the name of the data item, as shown next. This is
treated in exactly the same way by the compiler.
01 EMPLOYEE-RECORD.
03 PIC X(37).
03 EMPLOYEE-GENDER PIC X(01).
03 PIC X(135).
Previously, we have defined data characteristics using the PICTURE clause. Next we
look at ways to indicate different storage methods when defining data items. This is
done with the USAGE clause. The USAGE clause tells the computer how to represent
numbers internally. Here are some USAGE clauses:
NOTE: A clause — PIC 9(4) — is part of a statement. This is similar to English where a
clause is part of a sentence.
By default when usage is not specified, numeric data is held in the format we have seen
previously, as shown in the following example.
This is known as USAGE Display. The number is stored as one digit per byte, in just the
same way as an alphanumeric (PIC X) or alphabetic (PIC A) field. Such fields work
perfectly well, in that you can carry out any arithmetical operations on them that you
wish.
Yet, other methods of storing numeric data are more efficient (the calculations take
place more rapidly) and the data takes up less room. Programmers tend to use such
data types for internal data items (ones say in WORKING-STORAGE) or on records
where space is a priority.
Display Format
The first of these methods (Usage Display) allocates one byte per digit, which is what
we are familiar with. If the number contained "1234", it would be stored like this.
Binary Format
The Binary Format method allocates the actual number of bits needed to hold the
maximum number (9,999) and then rounds that up to the next byte. So, 9999 (binary
10011010101011) can be held in 14 bits and would occupy 2 bytes (16 bits). The
number "1234" would be stored as shown in the following.
10011010010 in binary
4D2 in hex
Storing data in binary numeric fields is the most efficient. However, there is a tradeoff
for gaining efficiency in this way. It is more difficult to know how much space a COMP
numeric field requires, for example, if the field is in a record. The following table lists
the space required for various COMP fields on the PC not mainframe.
The third method (Packed-Decimal Format) is in some ways a hybrid between the first
two. The number is stored or packed more compactly than in display format, and it can
be accessed with most of the extra efficiency of a binary field, yet it is still possible to
see its value if you look at the field's hex representation. The space allocated is one half
byte for each digit and one half byte for the sign (whether the field is signed or not). If
necessary, the number of bytes is then rounded up to the next whole number.
An example will make things clearer. A data item to hold 9(4) COMP-3 will consist of
two and a half bytes – four half bytes for the digits and half a byte for the sign. The two
and a half is then rounded up to three bytes. The number '1234' would be stored as
follows.
In the example above note the 'F' for the sign. If a number is unsigned, as here, then
this last half byte will always be hex F (binary 1111). If the number is signed, then the
last half byte will be one of the following:
• If the number is positive, hex C (binary 1100) with the "C" representing
"Credit.")
• If the number is negative, hex D (binary 1101) with the "D" representing
"Debit."
To calculate the length of a COMP-3 field, divide the number of digits (PIC 9s) by 2, and
then round up to the next whole byte if necessary.
It is often useful to be able to preset the value of one or more data items, so that these
values are in place before any code in the PROCEDURE DIVISION is executed. This is
done with the VALUE clause along with literals or constants.
Numeric Literals
The values shown in lines two and three are known as numeric literals. In the case of
PIC X or PIC A fields, the values must be enclosed in quotes. In the above example, WS-
NAME will now contain "SMITH" followed by fifteen spaces. WS-SALARY will contain
027500.00. In other words, if the value of the literal does not fill up the field, then
character fields are padded with spaces on the right and numeric fields padded with
zeros on the left.
Entering too large a value into a field (for example, giving WS-NAME a value of
"General Dwight D Eisenhower") causes the COBOL compiler to identify an error.
Also, use a numeric literal that matches the PICture of the item. Since WS-SALARY is
defined as PIC 9(6)V99, (the first 9 indicating a numeric data type), it would be wrong
to assign SALARY a VALUE of "NONE" (which is an alphabetic value).
Figurative Constants
In addition to numeric literals, you can use VALUE clauses with figurative constants.
Figurative constants preset data items to useful values. The following figurative
constants are available.
01 WS-RECORD.
03 WS-NAME PIC X(20).
03 WS-ADDRESS PIC X(100).
03 WS-SALARY PIC 9(5)V99.
Group fields are always regarded as being alphanumeric (even if all the Elementary
Level fields are numeric). In this case, WS-SALARY will end up set to spaces, which
might not be appropriate.
ZERO (or ZEROES) can be similarly thought-provoking. ZERO will put that value in the
field or fields referred to by the VALUE clause and format it using the PICture
information.
The following clause will move "character zero" (hex 30) into each of the bytes of WS-
NUMBER.
Another example will result in binary zero (hex 00) throughout the two bytes of the
field, as shown next
This results in a nasty little trap that is very easy to fall into.
In the following sample, the programmer is trying to set all four items in the group to
zero. Instead, ZERO gets moved to the group. Because the group is alphanumeric, hex
30 is moved to each of the eight bytes, setting all the items to 2336! (hex 3030). Note:
Each field defined in the example as PIC 9(4) COMP is stored as 2 bytes.
01 WS-NUMBERS.
03 WS-NUM-1 PIC 9(4) COMP.
03 WS-NUM-2 PIC 9(4) COMP.
03 WS-NUM-3 PIC 9(4) COMP.
03 WS-NUM-4 PIC 9(4) COMP.
To avoid the previous dilemma, use the LOW-Values figurative constant, which sets a
field to its lowest possible value. Preset the group to "binary zero." The following
example shows the correct way of achieving the goal.
01 WS-GROUP.
03 WS-NUM-1 PIC 9(4).
03 WS-NUM-2 PIC 9(4).
03 WS-NUM-3 PIC 9(4).
03 WS-NUM-4 PIC 9(4).
Use LOW-VALUES to set a field or field to its lowest possible value. HIGH-VALUES is the
opposite. HIGH VALUES moves hex FF to every byte of the field or fields in question.
The ALL "literal" presets a field with a character value, as shown in the following
example.
Important note: Use the VALUE clause as shown above only in the Working-Storage
Section.
• COPY data
• COPY and REPLACE data
• REDEFINE data
Sometimes working storage items should be common across different programs. How
do you handle these items?
Rather than repeat the definition in each case, use the COPY statement.
Let's look at an example where all programs in a payroll suite need the following same
definitions for working storage counters.
01 WS-COUNTERS.
03 WS-NUMBER-OF-EMPLOYEES PIC 9(4).
03 WS-TOTAL-SALARY PIC 9(8)V99.
03 WS-TOTAL-TAX PIC 9(7)V99.
03 WS-TOTAL-DEDUCTIONS PIC 9(6)V99.
In the following example, the file was saved as counters.cpy. The COPY statement is
typically inserted in Area A, though it is also valid in Area B.
PROCEDURE DIVISION.
This results in the following code.
COPY "COUNTERS.CPY".
01 WS-COUNTERS .
03 WS-NUMBER-OF-EMPLOYEES PIC 9(4).
03 WS-TOTAL-SALARY PIC 9(8)V99.
03 WS-TOTAL-TAX PIC 9(7)V99.
03 WS-TOTAL-DEDUCTIONS PIC 9(6)V99.
PROCEDURE DIVISION.
Sometimes full stops (periods) are absolutely necessary. The full stop at the end of a
COPY statement must never be omitted. In the above example, such an omission would
cause the compiler to be unable to find the Procedure Division.
You can create a "generic" copy file, where the actual names of the data items can be
modified to suit the particular program (rather than having to use the names in the
copy file). This is done using tags. The copy file can then be imported into a program,
and the (TAG) element can be replaced with another value.
01 (TAG)-NUMBERS .
03 (TAG)-NUM-1 PIC 9(4).
03 (TAG)-NUM-2 PIC 9(6).
03 (TAG)-NUM-3 PIC 9(5).
COPY "WS.CPY"
REPLACING ==(TAG)== BY ==WS01==.
COPY "WS.CPY"
REPLACING ==(TAG)== BY ==WS02==.
01 WS01-NUMBERS.
03 WS01-NUM-1 PIC 9(4).
Copyfiles are commonly used to hold data hierarchies, such as records. They can also
be used to hold executing code, that is, statements that are found in the Procedure
Division (discussed in the Procedure Division module later).
Let's say that we have a group of items in Working-Storage, perhaps copied in from a
record that was read. One of those fields is normally a numeric birthdate. However, if
the birthdate is not known, the field needs to be alphanumeric, so that it can contain
some other value, as shown in the following code.
01 WS-RECORD.
03 WS-NAME PIC X(30).
03 WS-DOB PIC 9(8).
03 WS-DOB-XX REDEFINES WS-DOB
PIC X(8).
The redefining item must immediately follow the one being redefined. Redefine items
at any level (except 01 in the File Section, as we shall see later). A redefined item can
have a VALUE clause, but the redefining clause cannot.
01 WS-INPUT-AREA-1.
03 EMPLOYEE-NAME PIC X(20).
03 EMPLOYEE-ADDRESS PIC X(60).
03 EMPLOYEE-SALARY PIC 9(6)V99.
(and so on)
01 WS-INPUT-AREA-2.
03 EMPLOYEE-NAME PIC X(20).
03 EMPLOYEE-DOB PIC 9(8).
The COBOL compiler tolerates any number of identically-named fields, until it has to
distinguish between them. For example, the following statement from the Procedure
Division will be flagged as an error.
To anticipate a later portion of this course, the MOVE statement moves a value ("VLAD
THE IMPALER") to a data item (EMPLOYEE-NAME). Here the compiler cannot complete
the MOVE, as the data name is not unique. The solution is to qualify the data name,
making it unique, as shown by the addition of "WS-INPUT-AREA-2."
3.13. Summary
In this module we have examined the following topics:
Details
Define this
Using this value…
field…
EMPLOYEE-TYPE 1 alphanumeric digits
EMPLOYEE-NAME 20 alphanumeric digits
7 numeric digits 2 of which are
EMPLOYEE-SALARY
decimals
EMPLOYEE-NO 6 numeric digits
4. On the next 03 level, create a group name of EMPLOYEE-LEAVING-DATE to include the following
fields:
MAINLINE SECTION.
MOVE SPACES TO EMPLOYEE-REC-N.
DISPLAY EMPLOYEE-REC-N
MOVE ZEROS TO EMPLOYEE-REC-N.
DISPLAY EMPLOYEE-REC-N.
MOVE LOW-VALUES TO EMPLOYEE-REC-N.
DISPLAY EMPLOYEE-REC-N.
MOVE HIGH-VALUES TO EMPLOYEE-REC-N.
DISPLAY EMPLOYEE-REC-N.
MOVE ALL "A" TO EMPLOYEE-REC-N.
DISPLAY EMPLOYEE-REC-N.
GOBACK.
6. Save the program in the X:\CLASS\MODULE3\NETX directory and provide the program name
PROGRAM3.CBL
• Explain the DISPLAY, ACCEPT, STOP, MOVE, and PERFORM verbs used in the
PROCEDURE DIVISION.
• Describe how the verbs PERFORM … IF, PERFORM … UNTIL, and GO TO can be
used to direct program logic.
• Write a basic COBOL program using a combination of verbs.
4.2. Introduction
Recall that the PROCEDURE DIVISION is the final division. It is the place where all the
code is written. The PROCEDURE DIVISION heading should start in Area A (columns 8-
11). Code written in this division should be contained in paragraphs, each of which
again starts in Area A. Typically paragraphs should appear in a SECTION.
This module discusses verbs that are used within the PROCEDURE DIVISION.
The following code provides a complete (though very simple) program and shows the
PROCEDURE DIVISION.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE41.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
WORKING-STORAGE SECTION .
01 WS-MESSAGE PIC X(20).
01 WS-NAME PIC X(20).
PROCEDURE DIVISION .
MAINLINE SECTION.
THE-ONLY-PARAGRAPH .
MOVE "HELLO WORLD" TO WS-MESSAGE .
The PROCEDURE DIVISION contains statements that are executed one by one (with
exceptions that we shall see later).
HELLO WORLD
Please enter your name:
Hiram Holliday
Nice to meet you, Hiram Holliday
That's all, folks
• The last statement in a paragraph or section must have a period or full stop, as
shown in the following example.
SECTION-6 SECTION.
PARA-6. [Paragraph header]
DISPLAY "I JUST MOVED 10 TO THE TOTAL".
PARA-7. [End of paragraph]
EXIT
• All statements can end with a period or full stop, so it is never incorrect to
terminate in this way.
IF WS-ERROR-COUNT> 15 THEN
DISPLAY "TOO MANY ERRORS"
GOBACK.
READ [and so on]
The logic shown here stops the run if more than fifteen errors exist. Adding a period at
the end of the DISPLAY statement would cause the program to always stop, which is
not a correct solution. The positioning of the periods is very important.
For this reason, many verbs have an END- construction, indicating the end of the verb's
scope, as shown in the following example. Although optional, it is a good idea to
include an END-IF statement.
• DISPLAY
• ACCEPT
• GOBACK
• MOVE
• PERFORM … IF
• PERFORM … UNTIL
• GO TO
PARA-8.
MOVE 100000 TO WS-SALARY-1 WS-SALARY-2
DISPLAY "I JUST GAVE US A RAISE" [Line 3]
GOBACK.
In the examples below the numeric literal zero is moved to field WS-TOTAL and 2004 is
moved to the field WS-YEAR.
MOVE 0 TO WS-TOTAL
In this example the character literal HELLO is moved to the field WELCOME-MESSAGE.
These two examples use the figurative constants SPACES and ZERO.
Here are two examples showing how data items can be moved.
In this example the value of field WS-NAME is moved to two fields: OUTPUT-NAME-1
and OUTPUT-NAME-2.
In this example the value of the field WS-COUNTER is moved to the field STORE-
COUNTER.
Using MOVE
MOVE works in a totally predictable way. MOVEing to a character field (whether from a
character or numeric one) causes the target field to fill up from the left. If the target
field is longer, the remaining byte(s) on the right will be space-filled. If the target field is
too short, the original value will be truncated from the right. The following illustrates
the how sending field will start filling up the target field starting from the left.
This behavior applies also if the source field is alphanumeric or alphabetic. However,
runtime errors often occur when a MOVE fails because a program is trying to move
non-numeric data to a numeric field. This happens when input fields do not contain an
expected numeric value. If there is the slightest chance of such errors, for example if
input fields might not contain numbers, then validate them. This is discussed further in
a later module.
The previous two examples used sending and target fields of the same length. Consider
the following example when the target field length is more restricted than the sending
field length.
One would think that "ELIZABETH" becomes assigned to NAME_OUT; however, the PIC
X(4) limits the target field to "ELIZ" only.
Add code for move ws-char-1 to ws-char-2 thru 5 and w-number-2 thru 4.
IDENTIFICATION DIVISION.
PROGRAM-ID. Example42.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUMBER-1 PIC 9(6) VALUE 243.
01 WS-NUMBER-2 PIC 99.
01 WS-NUMBER-3 PIC 9(9).
01 WS-NUMBER-4 PIC 9(4).
01 WS-CHAR-1 PIC X(4) VALUE "ABCD".
01 WS-CHAR-2 PIC X(8).
This
Contains this value…
field…
WS-
000243
NUMBER-1
WS- 43 (the six digits 000243 were truncated on
NUMBER-2 the left)
WS-
000000243
NUMBER-3
WS-
0243
NUMBER-4
WS-CHAR-4 "0243 " ("0243" followed by eight spaces)
"000" (the six characters "000243" were
WS-CHAR-5
truncated on the right)
This
Contains this value…
field…
WS-CHAR-1 ABCD
WS-CHAR-2 "ABCD " ("ABCD" followed by four spaces
Assuming that the moves of non-numeric data to numeric fields are allowed in the last
two lines, the fields now contain the following values.
This
Contains this value…
field…
WS - CD (the four non-numeric characters ABCD are
NUMBER-2 truncated on the left)
WS-NUMBER-
00000ABCD (five leading zeros)
3
PERFORM paragraph-header
In the ninth line of the following program, the verb PERFORM executes INIT-PARA, a
subprogram that begins on line 12.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE43.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
WORKING-STORAGE SECTION .
01 WS-TIMES PIC 9(4).
01 WS-NUM1 PIC 9(4).
PROCEDURE DIVISION .
PROG SECTION.
PERFORM INIT-PARA .
PERFORM LOOP-PARA WS-TIMES TIMES.
PERFORM END-PARA .
INIT-PARA SECTION.
DISPLAY "A SIMPLE MULTIPLIER" .
DISPLAY "HOW MANY TIMES (1 TO 6)?" .
ACCEPT WS-TIMES .
IF WS-TIMES > 6 OR WS-TIMES < 1 THEN
MOVE 1 TO WS-TIMES
END-IF.
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
DISPLAY "FIRST NUMBER?" .
ACCEPT WS-NUM1 .
DISPLAY "SECOND NUMBER?" .
ACCEPT WS-NUM2 .
MULTIPLY WS-NUM1 BY WS-NUM2 GIVING WS-NUM3
DISPLAY "PRODUCT OF " WS-NUM1 " AND " WS-NUM2
" IS " WS-NUM3 .
LOOP-PARA-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY "MULTIPLICATION DONE " WS-TIMES " TIME(S)" .
DISPLAY "THANK YOU FOR THAT" .
END-PARA-EXIT.
GOBACK.
A SIMPLE MULTIPLIER
HOW MANY TIMES (1 TO 6)?
3
FIRST NUMBER?
873
SECOND NUMBER?
990
PRODUCT OF 873 AND 990 IS 864270
FIRST NUMBER?
526
SECOND NUMBER?
17
PRODUCT OF 526 AND 017 IS 008942
FIRST NUMBER?
499
SECOND NUMBER?
21
PRODUCT OF 499 AND 021 IS 010479
MULTIPLICATION DONE 3 TIME(S)
THANK YOU FOR THAT
The program logic can be seen in PROG. The verb PERFORM executes the subprogram,
INIT-PARA. Control passes to INIT-PARA and the program works through the INIT-PARA
statements. The ACCEPT verb in INIT-PARA gets a value that the program needs. Here,
ACCEPT gets the number of times the main program loop will occur.
This logic states that if a user enters 0 or a number greater than 6, ignore it and
substitute 1. (A later module provides a more detailed discussion of the format of IF.)
At the end of INIT-PARA we return to PROG, where the second statement appears:
PERFORM LOOP-PARA WS-TIMES TIMES. This paragraph executes a particular number
of times (at the end of which control returns to PROG).
If we now look at the code in LOOP-PARA, we see two more ACCEPT statements, which
give the program the numbers needed for the MULTIPLY statement. There is more to
MULTIPLY than can be covered here, but we should be able to see that the statement
calculates the product of two data items, places it in a third data item, and then
DISPLAYs it.
Note: Both the MULTIPLY and DIVIDE statements will be covered later. The Divide
statement format is very similar to the MULTPLY; DIVIDE one data filed by a second
data field placing the result in a third data field.
Eventually, LOOP-PARA completes for the last time, and control passes to the last
statement of PROG, which is PERFORM END-PARA. This statement initiates END-PARA.
The END-PARA paragraph contains two informational DISPLAYs and a GOBACK. Note
that there is no need to end the program in PROG, making the GOBACK the last
statement in END-PARA as perfectly acceptable.
You can use the PERFORM verb in several formats, including PERFORM…UNTIL. Use
PERFORM … UNTIL according to the following syntax.
We have looked at the simple use of PERFORM, which executes once. The PERFORM ...
UNTIL executes one or many times until the condition is true.
Here is an example of a program using PERFORM ... UNTIL. The output follows the
program.
NOTE: Use comments liberally to make the code more understandable. The compiler
ignores any line with an asterisk ('*') in column 7. Use this for comments.
IDENTIFICATION DIVISION.
PROGRAM-ID. Example44.
AUTHOR. TRAINING.
INSTALLATION. MARS.
DATE-WRITTEN. 18/10/2005.
DATE-COMPILED.
SECURITY. MICROFOCUS
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNTER PIC 9.
PROCEDURE DIVISION.
*
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA UNTIL WS-COUNTER > 4.
PERFORM END-PARA.
INIT-PARA SECTION.
****************************************
* Initial actions
****************************************
DISPLAY "IN INIT PARA".
MOVE ZERO TO WS-COUNTER.
*
END-PARA SECTION.
DISPLAY "IN END PARA - STOPPING".
END-PARA-EXIT.
GOBACK.
IN INIT PARA
DOING LOOP PARA....1
DOING LOOP PARA....2
DOING LOOP PARA....3
DOING LOOP PARA....4
DOING LOOP PARA....5
IN END PARA - STOPPING
• PERFORM UNTIL is very commonly used, but the condition must become true at
some point or the program could loop forever. In this case, if the ADD 1 TO WS-
COUNTER were not present, this would happen. Another common mistake is for
the test not to be met because it has been incorrectly specified. For example,
the following code would generate a perpetual loop if the salary paid never
exactly equalled 20,000 — a better test would probably be greater than some
figure.
• PERFORM LOOP-PARA UNTIL SALARY-PAID = 20000
• By default, the UNTIL clause is tested before the loop. The loop happened five
times, because "greater than 4" becomes true only after the fifth iteration.
When the program comes round for the sixth time, "greater than 4" is now
true.
• There is a different format of the verb, PERFORM WITH TEST AFTER UNTIL . This
means that the will always be PERFORMed at least once. The default version of
PERFORM with the test before is very useful, as the loop will stop if the
condition is never true. For example, if you are reading records from a file, you
might write pseudo-code such as "perform process-record until there are no
more records on the file." The WITH TEST AFTER version of the verb would
assume that at least one record existed. So, if there were no records on the file,
a logic error would occur.
PERFORM works predictably in that control passes to a paragraph and then returns to
the statement after the PERFORM. bPERFORM can be used with multiple paragraphs,
as illustrated in the following program.
PROG.
PERFORM PARA-3 THRU PARA-5.
****** some other code
GOBACK.
PARA-3.
* code
PARA-4.
* code
PARA-5.
* code
The programmer could later add another paragraph (or multiple paragraphs), as shown
in the following code.
PARA-3.
* code
PARA-3A.
* new code
PARA-4.
Because the program lists PARA-3A between the start- and end-points of the
PERFORM, it will be executed. This is not necessarily a problem; indeed it can appear a
very convenient way of inserting extra code without altering the main logic of the
program. However, someone looking at the original PERFORM statement might not be
aware of the change. This illustrates the need for inserting comments in the code at
that point.
Always include a GOBACK in your code. In the previous example, if it were omitted,
after the PERFORM of the three paragraphs, the program would look what to do next.
The program would fall into PARA-3 and so on. This is not the fault of COBOL; rather, it
is the predicted behavior.
IDENTIFICATION DIVISION.
PROGRAM-ID. Example45.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
PERFORM, on the other hand, always jumps back to the place from which it came.
Apart from anything else, this makes the program logic easier to visualize and so less
prone to error.
IDENTIFICATION DIVISION.
PROGRAM-ID. Example46.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
PROG.
PERFORM PARA-1 THRU PARA-3.
Even in this small piece of code, it has become difficult to follow the logic. Although the
PERFORM results in control jumping to PARA-1, the GO TO then takes us to PARA-4, and
then we drop through to PARA-5. In fact, everything works out all right, as the GO TO
PARA-3 takes us back to the last paragraph to be PERFORMed. Control can therefore
return to the GOBACK. If that GO TO pointed to, say a PARA-8 or there was no GO TO,
the results would be unpredictable, to say the least.
IN PARA 1
IN PARA 4
IN PARA 5
IN PARA 3
4.12. Summary
In this module we have examined the following PROCEDURE DIVISION verbs:
• DISPLAY
• ACCEPT
• GOBACK
• MOVE
• PERFORM … IF, PERFORM … UNTIL
• GO TO
• PERFORM
• DISPLAY
• IF - MOVE
• ACCEPT
• DIVIDE
• GOBACK
Details
4. In the PROCEDURE DIVISION create 4 paragraphs. In the first paragraph list the PERFORM
5. In the 2nd paragraph list 2 DISPLAY statements for the title and question for the program; and the
accept statement for the WS-TIMES field which defines the number of problems to be computed.
6. Include in the 2nd paragraph an IF statement that specifies that if the number of times is less that
Use this
To…
Verb…
DISPLAY Prompt user for first number.
ACCEPT Read in value for WS-FIRST-NUMBER.
DISPLAY Prompt user for second number.
ACCEPT Read in value for WS-SEOND-NUMBER.
IF Test WS-SECOND-NUMBER for 0.
Assign .01 to WS-SECOND-NUMBER if IF
MOVE
condition is true.
Notify user second number was replaced with
DISPLAY
.01.
DIVIDE Divide WS-FIRST-NUMBER
BY Provide WS-SECOND-NUMBER
GIVING Calculate WS-RESULT
ROUNDED Round to whole number.
DISPLAY Output results.
Use this
To…
Verb…
Notify user calculations have been preformed
DISPLAY
"WS-TIMES" times.
DISPLAY Display concluding message.
GOBACK End program.
5.1. Objectives
Upon successful completion of this module, you will be able to:
COBOL programs tend to be very long-lived, perhaps for twenty or thirty years. Over
that time they may undergo revision, from the addition of minor features to almost a
total rewrite. A well-designed program from the start makes modifications considerably
easier. If the modifications are in turn well designed, then the task is simplified for the
next maintenance programmer, and so on.
There are many different ways of writing a COBOL program. We will examine one
method that works by expressing the structure of anything, whether or not computer-
related, in terms of the following structures. We can use this method to design a
program visually.
The following illustration represents one structure. A single box indicates the entire
structure to be defined.
The next illustration shows multiple constructs that make up the structure. One or
more constructs might appear below the whole structure. Two or more boxes in a
horizontal line (a group of items or events) are known as a sequence.
One box containing an asterisk in the top right-hand corner represents an iteration.
This indicates multiple occurrence of whatever is below. The number of occurrences or
the condition when there are no more occurrences might show above the box. Note
that the number of occurrences can be zero. The following illustration shows an
iteration of the train carriage item.
Different types of boxes cannot be at the same level in the same part of the structure.
We can now see how to apply these structures to data files, and from those structures,
how to design a logically correct program. We have not yet covered the COBOL entries
needed to cope with file and record handling. But, we can complete the design here
and then write programs in a later module, secure in the knowledge that we have
mastered the underlying logic.
Let us say that we want to design a program that reads in a file of records and then
deals with them in different ways depending on the type of record we find. Let's use
the following rules for our example.
• The input file can include any number of records (including 0).
• There are two types of input records, type A and type B. We will assume that
there is a PIC X field on the record.
• If we find a type A record, create an output record based on that record, and
write it out. (The details of the fields are not relevant at the design stage.)
• If we find a type B record, just add to a type B counter, which will display at the
end of the program.
The '*' indicates that the program includes an iteration of record (zero or more). We
can worry about how the program deals with zero records later. The circle in the top
right-hand of the bottom boxes represents a selection (only one of the boxes at this
level can be true at one time).
The output file is similar, but simpler, as shown in the following illustration.
Program Actions
The structure also contains actions that the program will need. Practically any program
reading or writing to a sequential file (records following one another) will have a very
similar design, depicted in the following illustration.
Writing this in a mixture of COBOL and pseudo-COBOL yields something like the
following code.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE52.
AUTHOR. TRAINING.
INSTALLATION. MARS.
DATE-WRITTEN. 18/10/2005.
DATE-COMPILED.
SECURITY. MICROFOCUS
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT TRAIN-IN ASSIGN "EXAMPLE52.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS TRAININ-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD TRAIN-IN.
01 TRAIN-REC.
03 TRAIN-TYEP PIC X .
03 TRAIN-NAME PIC X(20) .
03 TRAIN-NO PIC 9(6).
03 TRAIN-AGE PIC 9(6) .
WORKING-STORAGE SECTION.
01 TRAININ-FILE-STATUS PIC X(02).
01 WS-END-OF-FILE PIC 9 VALUE 0.
01 WS-TYPE-B-COUNTER PIC 9(6) VALUE 0.
PROCEDURE DIVISION.
PROG.
PERFORM INIT-PARA.
PERFORM BOD-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "START OF PROGRAM".
[open files]
BOD-PARA SECTION.
PERFORM UNTIL WS-END-OF-FILE = 1
PERFORM PROCESS-REC
END-PERFORM.
BOB-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY WS-TYPE-B-COUNTER
" 'B' RECORDS READ".
[close files]
END-EXIT.
GOBACK.
PROCESS-REC SECTION.
IF [it's a type A record] THEN
PERFORM TYPE-A-ACTIONS
ELSE [we're assuming here only A or B]
PERFORM TYPE-B-ACTIONS
END-IF.
PROCESS-REC-EXIT.
EXIT.
TYPE-A-ACTIONS SECTION.
[set up output record]
[write output record ]
[read file, if no record, move 1
to flag]
TYPE-A-ACTIONS-EXIT.
EXIT.
TYPE-B-ACTIONS SECTION.
ADD 1 TO WS-TYPE-B-COUNTER.
[read file, if no record, move 1
to flag]
TYPE-B-ACTIONS-EXIT.
EXIT.
• Firstly, the IF statement this time has an ELSE. The actions here will be carried
out if the condition is false (in other words, any record which isn't a type A will
automatically be treated as a type B). This may well be acceptable. Alternatively
you may need to check for A, B, and 'anything else'. Conditions are discussed in
far greater detail in another module.
• Secondly, on this occasion the IF statement has been terminated with an END-
IF. Normally this means that no period (full stop) is needed at the end of the IF.
But, because this is the last statement in the paragraph, the period is necessary.
1. A dinner party with a number of guests, each of whom is either male of female.
3. The meal at the dinner party. The starter is quail's egg salad, langoustines or vegetable terrine.
Next, include a main course of steak (well-done, medium rare, or rare) with vegetables (and either
French fries or salad) or spinach and aubergine bake. Guests may drink one or more glasses of wine with
this portion of the meal. Peaches in Armagnac or lime and durian ice cream make the final course. (Both
Finally, everyone can have one or more cups of coffee (black or with cream) or a liqueur.
4. The train (shown previously), now more complex, with first- and standard-class carriages, a buffet
5. A holiday that consists of a flight at the beginning and a return flight at the end. Between those
two events are days where the holidaymaker goes to the beach, takes a coach trip, or goes shopping.
6. An encyclopedia that consists of many volumes, each containing many articles. Each volume has a
table of contents and an index. A supplementary volume also has a table of contents and index of all the
7. A person's life, defined in different ways. Firstly, draw a structure showing the person going to
school (more than one, possibly) and college, getting a job, and having a working life, followed by
8. Draw a structure of what you do in a typical week, showing events such as meals, working,
watching television (or whatever you do instead), hobbies and so on. You will probably have to show
9. A COBOL program with a start, middle, and end. The start and end don't have to show any detail at
the moment, but in the middle we have to show that a number of records (possibly zero) will be
The purpose of this program is to reformat the employee records. This program will
also count the number of records read and reformatted. In addition, this program will
count and display any invalid records on the employee master file.
Details
• Read in a file of records, and then deal with them in different ways depending on the type of
record found. The input file may have any number of records (including 0).
• The records should all be of type A. Let us assume that this is a PIC X field on the record that we
can test. However, there may be records with other values on the file.
• If it finds a type A record, make an output record based on that record and write it out (the
details of the fields are not relevant at the moment). Also increment a type A record counter.
• If it finds any other type of record, display the entire contents of that record, and add to an
'other-type-of-record counter'.
• Display a start-up message at the beginning of the program. Display both counters at the end.
• Note: The input file description of the Employee-Leaving-Date must be commented out.
PROGRAM STARTING
NUMBER OF VALID RECORDS PROCESSED: 0002
NUMBER OF INVALID RECORDS: 0000
ALL DONE!
6.1. Objectives
Upon successful completion of this module, you will:
• Use the Environment Division and Data Division entries in a COBOL program
that uses input or output sequential files.
• Use the correct format of READ, WRITE, OPEN and CLOSE statements in the
Procedure division for sequential files, including testing for an end-of-file
condition.
• Describe how COBOL deals with multiple record types on the same file.
• Be able to design and code a typical COBOL program that handles sequential
files.
After the discussion, we shall take the program from the previous module and turn it
into a working COBOL program. We shall then examine the way that COBOL deals with
multiple record types, within an input or output file.
A sequential file is a file containing records that must be retrieved in the exact order as
within the file. This module discusses how to open and close sequential files.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "INPUTFILE.DAT". (windows)
SELECT INFILE ASSIGN INPUTFILE. (Mainframe)
While the Division and Section entries begin in Area A, the file entries should start in
Area B.
Although COBOL assumes that the file is a sequential one, we could say this explicitly.
To make things crystal clear, specify that the file is sequential, as in the following code.
If you use an ORGANIZATION clause, you must write at least the first and last words
(ORGANIZATION and SEQUENTIAL). The words IS and RECORD are both optional.
ORGANIZATION must be spelled with a Z.
Naming Files
You may be wondering what naming rules apply for the SELECT…ASSIGN clause. The
filename used inside the program (INFILE) must conform to normal COBOL
requirements. The external name (INPUTFILE.DAT) follows the normal Windows
practice of name/dot/three-character extension. The DAT extension shows that this is a
data file. Also, although the name is shown in upper case, at run-time the Windows
environment ignores the case when looking for the file.
The following code shows a slightly larger entry. This time it includes an input and an
output file, both sequential.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "INPUTFILE.DAT".
SELECT OUTFILE ASSIGN "OUTFILE.DAT".
Each file needs its own SELECT statement. Each of them should be terminated with a
period.
The input file must exist while an output file might not exist. If it does, by default the
program overwrites it and loses existing records.
The following program adds a FILE SECTION within the DATA DIVISION and a FILE
DESCRIPTIION (FD) to the program above. The DATA DIVISION almost always contains a
WORKING-STORAGE SECTION. If you use a FILE SECTION, it must precede WORKING-
STORAGE.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "INPUTFILE.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "INPUTFILE.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
* The input file
FD INFILE. [File Description]
01 INREC. [Record Description]
03 IN-REC-TYPE PIC X.
03 IN-NAME PIC X(20).
03 IN-DOB PIC 9(8).
* The output file
FD OUTFILE.
01 OUTREC.
03 OUT-NAME PIC X(20).
03 OUT-DOB PIC 9(8).
For every file name named in the SELECT statement within the INPUT-OUTPUT
SECTION of the ENVIRONMENT DIVISION, you must include an FD (File Description)
entry. The FD statement marks the beginning of the data description for that file. The
name following the FD must match that after the appropriate SELECT statement in the
INPUT-OUTPUT SECTION. Because two SELECT statements were included for two file
names in the previous code, the program must include two FD entries. Code FD entries
in Area A.
Record Structures
After coding the file description entries for both files, you need to define records within
each file. The record description indicates the record attributes and follows its file
description entry.
DATA DIVISION.
FILE SECTION.
* The input file
FD INFILE. [File Description]
01 INREC. [Record Description]
03 IN-REC-TYPE PIC X.
03 IN-NAME PIC X(20).
03 IN-DOB PIC 9(8).
Each record description must start with a Group Level item, which names the record. In
our example, these are 01 INREC and 01 OUTREC.
INREC shows the structure of a record on the input file, and OUTREC similarly
represents the fields on the output record. Only one record appears for each file. We
are going to deal only with one record at a time, so this is logical.
Important note: You cannot use the VALUE clause to preset fields in the File Section.
Why do you think this is? The VALUE clause cannot be used to preset fields in the File
Section, because the contents of the input and/or output data is moved into this area.
• OPEN
Before the program can access a file, it needs to be OPENed. Input files are OPENed
INPUT, and output files are normally OPENed OUTPUT. Use the OPEN verb in the
following format:
Two types of access include INPUT and OUTPUT; there are other types, but we need
only these two. The following code shows an example.
PROCEDURE DIVISION.
PROG.
[other code]
OPEN INPUT INFILE.
OPEN OUTPUT OUTFILE.
Notice again that the names used with the verbs are those associated with both the
SELECT and FD entries.
We could rewrite the above code as follows, where the words have been spaced for
clarity.
There is an alternative to OUTPUT for sequential files. You may remember that we
mentioned that by default COBOL clears output files if they exist. This is what OPEN
OUTPUT does. If you instead write OPEN EXTEND, a pointer is positioned at the end of
any existing records, and new records are added to the end of the file.
Use the OPEN statement to identify which files should be accessed and to make them
available to your program. If you do not OPEN a file, any program actions on the file
generate an error.
Logically, CLOSE statements belong at the end of the program logic, just as OPEN
statements belong at the beginning. The following code shows how we might use the
CLOSE verb.
CLOSE INFILE
OUTFILE.
CLOSE, like OPEN, can be used on many files at once. While files can be opened in
different ways, they are all closed in the same way. So, the format of the CLOSE
statement is simpler. Closing a file releases the file back to the operating system so that
other programs can use it.
Having opened the files, the program needs to know how to read a record. In fact the
program does not read a record; it reads a file. At the same time, the program should
indicate what happens if the read fails because there aren't any, or any more, records.
The following code shows how to use the READ verb and how to add an action when
the program reaches the end of the file.
READ INFILE
AT END MOVE 1 TO WS-END-OF-FILE
END-READ
In other words, whenever we write a READ statement for a sequential file, we always
specify the actions to be taken if the READ fails.
• If the READ succeeds, that is, if there is a record or another record, the contents
of that record will go into the 01 description specified. In our case, the contents
go into the record description of INREC as shown in our code previously. (This
area is known as the record buffer.) WS-END-OF-FILE will not be touched.
• If the read fails, WS-END-OF-FILE will be set to 1. The contents of INREC may be
undefined (if there has never been a record) or those of the last record read (if
there have been previous records). Logically, we should not look at them
anyway, because there is no new record to read.
Why do we read the file, rather than the record? It is possible that a file may contain
records of two or more types. Because this is an input file, we have no idea what
record type is coming in next. All we can do is read the file. We shall examine multiple
record types later.
Why do we WRITE a record? For the same reason that we READ a file: multiple record
types. Since this is output, we are creating the record. If there more than one type
exists, we need to specify which one we are writing. Again, we shall discuss this later.
Notice above that prior to WRITEing, we need to have built up the output record in its
record buffer. The data names on the input and output buffers have been made similar,
purely to lessen the chance of making mistakes when moving values.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE61.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION .
FILE-CONTROL.
***************************************************
* Notice in following Select statements that the
* names assigned to the files are the same names
* used in the FD statements; INFILE and OUTFILE.
***************************************************
SELECT INFILE ASSIGN "INFILE61.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
SELECT OUTFILE ASSIGN "OUTFILE61.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS OUTFILE-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
********************************
* A file-handling program
********************************
FD INFILE .
01 INREC .
03 INREC-TYPE PIC X .
INIT-PARA SECTION.
DISPLAY "START OF PROGRAM".
INITIALIZE WS-END-OF-FILE
WS-TYPE-B-COUNTER
*******************************************************
* Once the file is open, OPEN INPUT INFILE, the data
* can be accessed using the data names defined
* in the '01 INREC' under FD entry.
*******************************************************
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO THEN
DISPLAY "INVALID INFILE FILE"
INFILE-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE.
IF OUTFILE-FILE-STATUS NOT = ZERO THEN
DISPLAY "INVALID OUTFILE FILE"
OUTFILE-FILE-STATUS
GOBACK
END-IF
PERFORM READ-FILE.
INIT-PARA-EXIT.
EXIT.
BOD-PARA SECTION.
PERFORM UNTIL END-OF-FILE
IF INREC-TYPE = "A"
PERFORM TYPE-A-ACTIONS
ELSE
PERFORM TYPE-B-ACTIONS
END-IF
PERFORM READ-FILE
END-PERFORM.
BOD-PARA-EXIT.
EXIT.
The finished code is based strictly on the program structure we saw previously, and this
means that there is only a small amount of code in each paragraph. This means that it
is both easy to follow and to debug if any logic problems are detected later. In fact, if
the program is designed correctly, logic problems cannot occur, except through simple
transcription errors, such as writing the wrong paragraph in a PERFORM statement.
READ INFILE
AT END MOVE 1 TO WS-EOF
NOT AT END PERFORM GOT-A-RECORD
END-READ.
READ INFILE
AT END SET END-OF-FILE TO TRUE
NOT AT END PERFORM GOT-A-RECORD
END-READ.
READ INFILE
AT END [ ]
MOVE INREC TO WS-REC
END-READ.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE62.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION .
FILE-CONTROL.
SELECT INFILE ASSIGN "INFILE62.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
FD INFILE .
01 INREC PIC X(30) .
FD OUTFILE .
01 OUTREC PIC X(18).
WORKING-STORAGE SECTION .
01 WS-END-OF-FILE PIC 9.
88 END-OF-FILE VALUE 1 .
01 INFILE-FILE-STATUS PIC X(02).
01 OUTFILE-FILE-STATUS PIC X(02).
PROCEDURE DIVISION .
PROG SECTION.
PERFORM INIT-PARA .
PERFORM BOD-PARA .
PERFORM END-PARA .
INIT-PARA SECTION.
DISPLAY "START OF PROGRAM" .
INITIALIZE WS-END-OF-FILE
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID OUTFILE FILE"
INFILE-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE .
IF OUTFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID OUTFILE FILE"
OUTFILE-FILE-STATUS
GOBACK
END-IF
READ INFILE INTO WS-REC
AT END SET END-OF-FILE TO TRUE
END-READ.
INIT-PARA-EXIT.
EXIT.
BOD-PARA SECTION.
PERFORM UNTIL END-OF-FILE
Display "WS-USEFUL-PART = " WS-USEFUL-PART
WRITE OUTREC FROM WS-USEFUL-PART
* WRITE OUTREC FROM inrec
READ INFILE INTO WS-REC
AT END SET END-OF-FILE TO TRUE
END-READ
END-PERFORM.
BOD-PARA-EXIT.
EXIT.
END-PARA SECTION.
CLOSE INFILE
OUTFILE.
END-PARA-EXIT.
GOBACK.
If we had wanted only the first 18 bytes, this would have been even simpler. We would
keep the same record buffers (INREC and OUTREC) as above, but rewrite the code as
shown in the following program.
READ INFILE
AT END [ ]
Let's look at an example used for personnel information. Our input file can have one of
two record types: Type A or Type B.
DATA DIVISION.
FILE SECTION.
FD INFILE.
* Type 'A' record
01 INREC-TYPE-A.
03 INREC-TYPE PIC X.
03 INREC-NAME PIC X(20).
03 INREC-SALARY PIC 9(5)V99.
03 INREC-EMP-NO-A PIC 9(6).
* Type 'B' record
01 INREC-TYPE-B.
03 INREC-TYPE-B PIC X.
03 INREC-EMP-NO-B PIC 9(6).
03 INREC-LEAVING-DATE PIC 9(8).
In the Working-Storage section, two such 01s would signify that two separate items or
groups were being declared. That is not the case here.
The 01s map onto the same record. This is called implicit redefinition and makes the
handling of multiple record types very simple.
Let us assume that a record is read in. Which definition of the record – which 01 level –
does it end up in, A or B? The answer is both.
Having multiple 01 record types defined is like having many windows all looking onto
the same data – each allowing us to see the data in a different way.
To know which definition we should use, we just need to test the first byte of the
record, which in the previous program is given a name only in the first definition ("A"
or "B"). We can always test that byte using the name INREC-TYPE. If we have a record
type A, we use the fields appropriate to that record, and if type B, those fields instead.
Before looking at multiple output record types, let us design and code a program that
uses multiple input record types.
Let us say that we want to design a program that will read in a file of records and then
deal with them in different ways depending on the type of record we find. The
following describes the input record.
• The input file may have any number of records (including 0).
• There are two types of input records: type A and type B. (There is a PIC X field
on the record that we can test.)
• If we find a type A record, we create an output record on one file based on that
record and write it out. We also add 1 to a counter.
• If we find a type B record, we create an output record on another file based on
that record and write it out. We add 1 to a (different) counter.
This time there are two output files, as shown in the next illustration. Each record in
each file corresponds to one of the input records.
The counters have been set up as elementary items, set to binary zero by MOVING THE
VALUE LOW-VALUES TO the group field.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE63.
AUTHOR. TRAINING.
INSTALLATION. MARS.
DATE-WRITTEN. 18/10/2005.
DATE-COMPILED.
SECURITY. MICROFOCUS.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "INFILE63.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD INFILE.
01 INREC-TYPE-A.
03 INREC-TYPE PIC X.
88 TYPE-A-RECORD VALUE "A".
03 INREC-NAME PIC X(20).
03 INREC-SALARY PIC 9(5)V99.
03 INREC-EMP-NO-A PIC 9(6).
01 INREC-TYPE-B.
03 FILLER PIC X.
03 INREC-EMP-NO-B PIC 9(6).
03 INREC-LEAVING-DATE PIC 9(8).
FD OUTFILE-A.
01 OUTREC-A.
03 OUTREC-NAME PIC X(20).
03 OUTREC-EMP-NO-A PIC 9(6).
FD OUTFILE-B.
01 OUTREC-B.
01 WS-END-OF-FILE-FLAG PIC 9.
88 END-OF-FILE VALUE 1.
01 WS-COUNTERS.
03 WS-A-COUNTER PIC 9(4).
03 WS-B-COUNTER PIC 9(4).
03 WS-C-COUNTER PIC 9(4).
PROCEDURE DIVISION.
PROG SECTION.
PERFORM INIT-PARA.
PERFORM BOD-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "PROGRAM STARTING".
MOVE ZERO TO WS-COUNTERS
MOVE SPACE TO OUTB-FILE-STATUS
OUTA-FILE-STATUS
INFILE-FILE-STATUS
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID INFILE FILE"
INFILE-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE-A
IF OUTA-FILE-STATUS NOT = ZERO
DISPLAY "INVALID OUTFILEa FILE"
OUTA-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE-B
IF OUTB-FILE-STATUS NOT = ZERO
DISPLAY "INVALID OUTFILEb FILE"
OUTB-FILE-STATUS
GOBACK
END-IF
PERFORM READ-FILE.
INIT-PARA-EXIT.
EXIT.
BOD-PARA SECTION.
PERFORM UNTIL END-OF-FILE
IF TYPE-A-RECORD THEN
PERFORM TYPE-A-RECORDS
ELSE
PERFORM TYPE-B-RECORDS
END-IF
PERFORM READ-FILE
END-PERFORM.
BOD-PARA-EXIT.
TYPE-A-RECORDS SECTION.
MOVE INREC-NAME TO OUTREC-NAME
WRITE OUTREC-A.
ADD 1 TO WS-A-COUNTER.
TYPE-A-RECORDS-EXIT.
EXIT.
TYPE-B-RECORDS SECTION.
MOVE INREC-EMP-NO-B TO OUTREC-EMP-NO-B.
MOVE INREC-LEAVING-DATE TO OUTREC-LEAVING-DATE.
WRITE OUTREC-B.
ADD 1 TO WS-B-COUNTER.
TYPE-B-RECORDS-EXIT.
EXIT.
READ-FILE SECTION.
READ INFILE
AT END SET END-OF-FILE TO TRUE
END-READ.
READ-FILE-EXIT.
EXIT.
FD OUTFILE.
01 OUTREC-1.
03 OUTREC-NAME PIC X(20).
03 OUTREC-EMP-NO PIC 9(6).
01 OUTREC-2.
03 OUTREC-COUNTER PIC 9(4).
• The input file may have any number of records (including 0).
• All input records are treated the same way, and one normal output record is
created for each input record (so that reading 1000 input records should
guarantee that there are 1000 output records)
• As a safeguard to ensure data integrity, we need to write a final record to the
output file, containing a count of all records written (apart from itself). In other
words, if this last record contains 500, then there should be 501 records on the
file. If the input file should happen to be empty, then the output file will contain
this one special record, containing the number zero.
In terms of designing the program, the input file can be represented very simply, as
shown next.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE64.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INFILE ASSIGN "INFILE64.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
SELECT OUTFILE ASSIGN "OUTFILE64.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS OUTFILE-FILE-STATUS.
DATA DIVISION.
FD INFILE.
01 INREC.
03 INREC-EMP-NO PIC 9(6).
03 INREC-START-DATE PIC 9(8).
03 INREC-NAME PIC X(20).
FD OUTFILE.
01 OUTREC-1.
03 OUTREC-NAME PIC X(20).
03 OUTREC-EMP-NO PIC 9(6).
01 OUTREC-2.
03 OUTREC-COUNTER PIC 9(4).
WORKING-STORAGE SECTION.
01 WS-END-OF-FILE PIC 9.
88 END-OF-FILE VALUE 1.
01 WS-RECORD-COUNTER PIC 9(4).
01 OUTFILE-FILE-STATUS PIC X(02).
01 INFILE-FILE-STATUS PIC X(02).
PROCEDURE DIVISION.
PROG SECTION.
PERFORM INIT-PARA.
PERFORM BOD-PARA.
PERFORM END-PARA.
*******************************************************
Once the file is open, OPEN INPUT INFILE, the data
can be accessed using the data names defined
in the '01 INREC' under FD entry.
********************************************************
INIT-PARA SECTION.
DISPLAY "PROGRAM STARTING".
MOVE ZERO TO WS-END-OF-FILE
WS-RECORD-COUNTER
MOVE SPACE TO OUTFILE-FILE-STATUS
INFILE-FILE-STATUS
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO THEN
The purpose of this program is to read the employee records and create two files. One
will contain the 'normal' employee records, TYPE N. This file will be formatted different
than the employee record.
The second file created will contain all records on the employee file and a special
record for each employee leaving the company, TYPE X.
Both types of records created will be formatted different than the employee record.
This program will also count and display the number of records written to both output
files.
Details
• Read in a file of records, and then deal with them in different ways depending on the type of
record found. The input file may have any number of records (including 0).
• The input records will either be of type N (normal) or type X (leaving).
• An input record type N will have a one-character field for type, a twenty-character field for the
4. Some sort of start-up message should display at the beginning of the program. Record counters for
5. Save the program in the X:\CLASS\MODULE\NETX directory and provide the program name
PROGRAM6.
PROGRAM STARTING
NUMBER OF 'N' RECORDS PROCESSED: 0002
NUMBER OF 'X' RECORDS PROCESSED: 0001
ALL DONE!
Output-N
0001Vlad the Impaler 6587000228279
0002Joanna Lumley 4456977025611
Output X
10000018112005
0001
• Use the verb EVALUATE and the IF condition as well as the Level 88 construct in
testing conditions.
• Describe the different types of conditions.
• Explain the advantages and disadvantages of different ways of condition testing.
• Write a COBOL program using different types of condition testing.
In COBOL the flow of a program is controlled almost entirely by IF-ELSE statements, the
PERFORM verb, and the GO TO verb. Use the IF verb to make decisions, which change
the flow of a program.
IF [condition] (THEN)
[do one or more statements]
or
[NEXT SENTENCE] OR [CONTINUE]
(ELSE
[do one or more statements]
or
[NEXT SENTENCE])
OR
[CONTINUE]
(END-IF).
This code indicates that if a is true, then perform an action. Otherwise (if the condition
is false), do a different action. Lastly, END-IF marks the end of the conditional
statement.
We have met this statement, both with and without the ELSE.
NEXT SENTENCE means drop through to whatever follows this IF statement. It is less
• Relational conditions
• Class conditions
• Sign conditions
• Condition-name conditions (Level 88s)
• Compound conditions
• Nested conditions
Different types of data are evaluated differently. Alphanumeric data items and literals
are evaluated from left to right and spaces on the end are not evaluated. "ABC" is the
same as "ABC ". Numeric data is compared by values; 3.00 is equal to 3.
Relational Conditions
In previous modules we used relational conditions, when two operands are compared
with operators such as =, >, or <. Here is a sample evaluating whether WS-NUMBER is
greater than 4.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE71.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUMBER PIC 9(4)V99.
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A SIMPLE NUMEBER TEST".
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
DISPLAY "PLEASE ENTERED THE NUMBER?".
ACCEPT WS-NUMBER.
IF WS-NUMBER > 4 THEN
We can also use NOT to make each statement negative, as shown in the fourth line of
the previous code.
In some cases we might want to determine whether a data is not equal to something.
No equivalent exists for <>, which means not equal; however, we could express it in
one of the following ways.
Testing numbers is easy, but what about strings of different lengths? Which is greater,
"FRED" or "FREDA"? Fortunately the answer is predictable and consistent. The two
strings are tested character by character, starting at the left. For the first four bytes
"FRED" and "FREDA" are equal. At the fifth byte, "A" is compared with space, since
"FRED" does not contain any more real characters that can be checked. (This still
happens if the string holding "FRED" has no bytes that are unoccupied.) Since hex 20
represents space in the ASCII character set, and hex 41 represents "A", "FRED" is less
than "FREDA".
Class Conditions
Class conditions allow the programmer to test for a specific type of data, for example,
ALPHABETIC or NUMERIC. Such a test would use the following format.
Sign Conditions
Use sign conditions with numeric fields. Sign conditions allow the programmer to test
for POSITIVE, NEGATIVE, or ZERO. The following code shows how you could test for a
negative value.
…[for example]
01 WS-CUSTOMER-BALANCE PIC S9(5)V99.
Level 88 conditions use a condition name that can be tested. Condition names are also
called flags. For example, if FEMALE (the condition name), then perform the next
statement. Condition names are coded by writing the condition name followed by
information that indicates what value makes the condition true. For example, a
program might read a file and encounter a field that indicates gender as "F" or "M". In
the following statement, FEMALE is true if the value is "F".
The following code shows how to reference the INREC-GENDER field by using the field
name FEMALE or MALE. If FEMALE, the value of INREC-GENDER is equal to "F".
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE73.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-GENDER PIC X.
88 FEMALE value "F" "f".
88 MALE value "M" "m".
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A SIMPLE GENDER TEST".
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
DISPLAY "PLEASE ENTERED MALE OR FEMALE ?"
ACCEPT WS-GENDER.
IF FEMALE THEN
DISPLAY "Code entered is female"
END-IF
IF MALE THEN
DISPLAY "Code entered is male"
END-IF.
LOOP-PARA-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY "MANY THANKS!".
END-PARA-EXIT.
Use 88 levels also to set several valid values against a field, as shown in the following
code.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE74.
AUTHOR. TRAINING.
INSTALLATION. MARS.
DATE-WRITTEN. 18/10/2005.
DATE-COMPILED.
SECURITY. MICROFOCUS
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 INREC.
03 INREC-TYPE PIC X.
88 VALID-TYPES VALUE
"A" THRU "E" "G" "Z".
88 CEO VALUE "A".
88 VICE-PRESIDENT VALUE "B".
88 SENIOR-MANAGER VALUE "C".
88 MANAGER VALUE "D".
88 TEAM-MEMBER VALUE "E".
88 JANITOR VALUE "G".
88 TRAINER VALUE "Z".
03 INREC-SALARY PIC 9(6)V99.
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A EMPLOYEE SALARY".
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
DISPLAY "PLEASE ENTER EMPLOYEE CODE?"
ACCEPT INREC-TYPE .
IF NOT VALID-TYPES
DISPLAY "INVALID RECORD TYPE "
INREC-TYPE "!"
GOBACK
END-IF
IF CEO
MOVE 100 TO INREC-SALARY
The previous code shows several level 88s used on one field, firstly for validation. If the
field is not set to any of "A" to "E" ("THRU" can also be spelled "THROUGH"), "G", or
"Z", then the record is invalid. Once that test has been carried out, the field can be
tested for separate values, each of which can have a meaningful name.
Such use of level 88s can help avoid compound conditions, described in the next
section.
One very common use of the 88 levels is on the flag used to signify the end of the file,
as shown in the following code. Using LEVEL 88's encourages more English-like
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE75.
AUTHOR. TRAINING.
INSTALLATION. MARS.
DATE-WRITTEN. 18/10/2005.
DATE-COMPILED.
SECURITY. MICROFOCUS.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION .
FILE-CONTROL.
SELECT INFILE ASSIGN "INFILE75.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD INFILE .
01 INREC .
03 INREC-TYPE PIC X(01).
03 INREC-NAME PIC X(20) .
03 INREC-GENDER PIC X(01).
WORKING-STORAGE SECTION .
PROCEDURE DIVISION .
PROG .
PERFORM INIT-PARA .
PERFORM BOD-PARA .
PERFORM END-PARA .
INIT-PARA SECTION.
INITIALIZE WS-END-OF-FILE
WS-TYPE-B-COUNTER
INFILE-FILE-STATUS
WS-TYPE-A-COUNTER
As an alternative way to "switching on" the condition name, use the verb SET, which
changes a LEVEL 88 or the value of any field. In the example below, the SET moves a
value of 1 to WS-END-OF–FILE.
READ INFILE
AT END SET NO-MORE-RECORDS TO TRUE
END-READ
Compound Conditions
To test more than one condition at once, use compound conditions in your IF
statement, as shown in the following examples.
When an IF includes an OR condition, either condition can be true to make the entire IF
condition true. When code includes an AND condition, both conditions must be true.
OR conditions are evaluated after AND conditions
When using AND and OR in the same test always use bracketsso that the condition in
brackets is evaluated before anything else.
Nested Conditions
IF INREC-JOB-TITLE = "MANAGER"
IF INREC-SALARY > 50000
IF INREC-START-DATE < 20000101
[actions]
ELSE
[actions]
ELSE
[actions]
ELSE
[actions].
Such statements, even when indented as shown, can be difficult to understand (even
by the person who wrote them) and should be avoided. For example, the above three
IFs can be replaced by one (IF….AND….AND), as shown in the following revised code.
IF INREC-JOB-TITLE = "MANAGER"
and INREC-SALARY > 50000
and INREC-START-DATE < 20000101
[actions]
ELSE
[actions]
ELSE
[actions]
The EVALUATE statement looks at a series of nested IF statements that can have
compound conditions. The subsequent action depends on the results of these
evaluations.
Use the simple EVALUATE for several tests in one statement, as shown in the following
code.
EVALUATE INREC-TYPE
WHEN "A" THRU "D"
PERFORM MANAGER-STUFF
WHEN "E"
PERFORM OTHER-CODE
WHEN "G"
PERFORM JANITOR-CODE
WHEN "Z"
PERFORM TRAINER-FUNCTIONS
WHEN OTHER
PERFORM INVALID-RECORD
END-EVALUATE.
Although it is not required, always use END-EVALUATE at the end of your EVALUATE
statement to help make your program easier to read.
First, let us look at EVALUATE TRUE. The following code tests three conditions: end of
file, when the counter equals 99, or when another condition is true (the catch all).
When one of these conditions is true, then the EVALUATE is true and the condition-
specific action is taken
EVALUATE TRUE
WHEN END-OF-FILE
PERFORM UNEXPECTED-EOF
WHEN WS-COUNTER = 99
DISPLAY "Too many records"
GOBACK.
WHEN OTHER
PERFORM NORMAL-ACTIONS
END-EVALUATE.
To understand the code, let's look at the same code using IF statements.
IF JOB-TYPE = "MANAGER"
AND IN-SALARY > 250000 THEN
PERFORM REWARD-TOP-EXECUTIVES
ELSE IF JOB-TYPE = "TEAM LEADER"
AND IN-SALARY > 80000
PERFORM BENEFIT-MIDDLE-PEOPLE
ELSE IF JOB-TYPE ="EMPLOYEE"
AND IN-SALARY < 50000
PERFORM EMPLOYEE-BENEFITS
END-IF
END-IF
END-IF.
To indicate that a program should do nothing when a particular condition is true, use
the CONTINUE clause, which tells the program to execute the next statement after the
EVALUATE.
The following program code uses EVALUATE CONTINUE. If the program encounters
"MR", the CONTINUE clause indicates to do nothing with the MR data and continue
executing the next statement.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE77.
SECURITY. MICROFOCUS
ENVIRONMENT DIVISION.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 WS-CUSTOMER-TITLE PIC X(04).
88 MR-TITLE VALUE "MR" "mr".
88 FEMALE-TITLE VALUE "MRS" "mrs" "MISS"
"miss" "MS" "ms".
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A SIMPLE GENDER TEST".
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
DISPLAY "PLEASE ENTERED CUSTOMER TITLE?"
ACCEPT WS-CUSTOMER-TITLE.
EVALUATE TRUE
WHEN MR-TITLE
CONTINUE
WHEN FEMALE-TITLE
DISPLAY "FEMALE CUSTUMER"
WHEN OTHER
DISPLAY "INVALID-TITLE"
END-EVALUATE.
LOOP-PARA-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY "MANY THANKS!".
END-PARA-EXIT.
GOBACK.
• The input file may have any number of records (including 0).
• All input records contain a one-character type, a name field (20 characters), an
eight-digit start date, and an eight-digit salary field (six digits, two decimal
places).
• If the type field is set to anything other than "A", "B", "C", "E", "J" or "T", reject
the record and display the details. Similarly, if the name field is blank, reject the
record in the same way. Keep a count of all such invalid records.
• If the type field is "A", "B" or "C", write out the record details unchanged to an
output file. Another output file will contain unchanged details of all records
where the salary is greater than 75,000. So, a particular record may be on both
files.
• Keep record counts of types 'A' and 'C' (one count), 'B', 'E' and 'T' (another
count), and 'J' (a third count). Display these counts at the end of the program.
1. The multiple choices due to the conditions will not necessarily have to mean
separate paragraphs in COBOL, as shown in the design. However, it is best to show all
the important details at the design stage, simplifying things later if appropriate.
2. The Valid Start box holds the code for adding to the appropriate valid record
count.
3. The boxes Record Body and Record End have been inserted, because this helps
greatly with the next Read of the input file. In the diagram, it is tempting to put an
action "4" (read) on all the Yes and No boxes, as well as on the Invalid box. However,
this is incorrect, because the second condition would never be tested. Creating the
Record End box gives you somewhere to do the Read. In other modules we shall see
that a Record Start box can be similarly useful.
The following code implements the design, using both IF and EVALUATE statements.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE78.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL .
SELECT INFILE ASSIGN "INFILE78.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
SELECT OUTFILE-ABC ASSIGN "ABCOUT78.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS ABCOUT-FILE-STATUS.
SELECT OUTFILE-BIGSAL ASSIGN "BIGSALOUT78.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
PROCEDURE DIVISION .
PROG SECTION.
PERFORM INIT-PARA .
PERFORM BOD-PARA .
PERFORM END-PARA .
********************************************************
* INIT-PARA is only performed on time. It opens the
* input and output files and reads the first record.
******************************************************
INIT-PARA SECTION.
DISPLAY "STARTING" .
INITIALIZE COUNTS
BIGSALOUT-FILE-STATUS
INFILE-FILE-STATUS
ABCOUT-FILE-STATUS
WS-EOF
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID INFILE75 FILE"
INFILE-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE-ABC
IF ABCOUT-FILE-STATUS NOT = ZERO
PERFORM REC-END.
INIT-PARA-EXIT.
EXIT.
BOD-PARA SECTION.
PERFORM UNTIL END-OF-FILE
PERFORM PROCESS-REC
END-PERFORM.
BOD-PARA-EXIT.
EXIT.
*********************************************************
* END-PARA is only perform when all the records on
* the file have been read
********************************************************
END-PARA SECTION.
DISPLAY "FINISHED" .
DISPLAY "NUMBER OF 'A' AND 'C' RECORDS: "
WS-A-C-COUNT .
DISPLAY "NUMBER OF 'B', 'E' AND 'T' RECORDS: "
WS-B-E-T-COUNT .
DISPLAY "NUMBER OF 'J' RECORDS: "
WS-J-COUNT .
DISPLAY "NUMBER OF INVALID RECORDS: "
WS-INVALID-COUNT .
CLOSE INFILE
OUTFILE-ABC
OUTFILE-BIGSAL .
END-PARA-EXIT.
GOBACK.
PROCESS-REC SECTION.
PERFORM REC-BOD.
PERFORM REC-END.
PROCESS-REC-EXIT.
EXIT.
********************************************************
* REC-BOD is performed on each input record
* It checks for a valid record code
********************************************************
REC-BOD SECTION.
IF VALID-REC AND IN-NAME UNEQUAL SPACES THEN
PERFORM VALID-RECORD
ELSE
PERFORM INVALID-RECORD
END-IF.
REC-BOD-EXIT.
EXIT.
********************************************************
Note that in the case of the output files, there was no need to define any of the record
fields, because in both cases the record is a copy of the input. WRITE FROM can be
used for the same reason.
In the following code, the WRITE FROM moves the contents from INREC into the record
area defined by OUTREC-ABC before writing the record. This requires less coding in the
FILE SECTION.
7.7. Summary
In this module we have examined the following topics:
Introduction
Erasers Ltd sells office supplies ordered over the Internet. Companies can pay to
become a member of a scheme that gives them increased discounts. The discount
awarded depends upon how much they purchased in the previous 12 months, the
number of items in their current order, and their membership status.
Details
4. CURRENT-RECORD SECTION
5. NOT-CURRENT-RECOR
Write an EVALUATE statement that implements the logic in the decision table below to calculate the
For customers with a valid membership use the following discount structure:
STARTING
FINISHED
NUMBER OF CUSTOMER: 0008
NUMBER OF ORDERS : 0033
TOTAL VALUE OF ORDERS 000292.30
The purpose of this program is to read the employee records and create three output
files. Each will contain records of employee's within a range of salaries; either low
medium or high.
This program should also count and display the number of employee's in each
category.
Additionally, this program should evaluate the "TYPE" code on each record, rejecting
and displaying invalid records.
Details
• Read in a file of records, and then deal with them in different ways depending on the type of
record found. The input file may have any number of records (including 0).
• All input records contain a one-character type, a name field (20 characters), an eight-digit start
date, and an eight-digit salary field (six digits, two decimal places).
• If the type field is set to anything other than "A", "B", "C", "E", "R" or "T", reject the record and
display the details. Similarly, if the name field is blank, reject the record in the same way. Keep a
count of all such invalid records.
• If the salary is less than 25,000, write the record details unchanged to an output file. At the end
of the program write a special record to that file containing the total number of records written
(excluding that one). This record could contain the number zero.
• Include in another output file unchanged details of all records where the salary is between
25,000 and 64,999.99. Again, write a special record, containing the number of this type of
record written.
• Include all unchanged details of all other salaries in a third output file. Again, write a special
record at the end of the program.
4. Save the program in the X: \CLASS\MODULE7\NETX directory and provide the program name
PROGRAM72.CBL .
8.1. Objectives
Upon successful completion of this module, you will be able to:
• The INITIALIZE verb, which can be used on both numeric and character data.
• The arithmetic verbs ADD, SUBTRACT, MULTIPLY, DIVIDE and COMPUTE.
• The string handling verbs INSPECT, STRING and UNSTRING. This chapter also
explains how to extract a substring from a string using "reference modification."
Because this can be done with one or more VALUE clauses, INITIALIZE can be used
anywhere in the program. So, it can be executed many times during the program.
Furthermore, it will always "intelligently" decide what constitutes a sensible initial
value. INITIALIZE sets alphanumeric data items to spaces and numeric data items to
zeros, if no other value is specified.
The example below illustrates how INITIALIZE determines the initial value depending
on the field's attributes.
01 WS-DATA-ITEMS.
03 WS-NAME PIC X(20).
03 WS-SALARY PIC 9(6)V99.
03 WS-RECORD-COUNT PIC 9(4) COMP.
03 FILLER PIC X(40).
03 WS-ADDRESS PIC X(80).
[other code]
INITIALIZE WS-DATA-ITEMS.
In this example, WS-NAME and WS-ADDRESS will be set to spaces. WS-SALARY will be
set to zeros and WS-RECORD-COUNT to binary zeros.
The forty-character filler will be left untouched. INITIALIZE is therefore a very useful
and powerful verb.
It is also possible to INITIALIZE a field or fields to a different value, as shown in the next
example that uses the REPLACING clause. The REPLACING clause lets you specify the
type of field that should be initialized.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE81.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DATA-ITEMS.
03 WS-NAME PIC X(20).
03 WS-SALARY PIC 9(6)V99.
03 WS-RECORD-COUNT PIC 9(4) COMP.
03 FILLER PIC X(40).
03 WS-ADDRESS PIC X(80).
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
This statement will set WS-NAME and WS-ADDRESS to twenty and eighty "?"
characters, respectively. WS-SALARY will contain 6666.00 (not 6666.66) and WS-
RECORD-COUNT will contain 6666. Again, the FILLER will be left untouched.
2. COBOL does not automatically check for the result of a calculation going out of
range. However, you can specify that this check takes place. Should an out-of-range
event occur with no check in place, the results of the calculation are undefined.
Format 1: Simple
This means that the operand or operands to the left of the "TO" are added to the field
or fields to the right of the "TO," incrementing them. The fields or literals to the left are
unchanged. The receiving field or fields (to the right of the "TO") are formatted
according to their PICTURE clauses.
Using ROUNDED, not surprisingly, rounds the answer according to the number of
decimal places specified in the receiving field (though rounding errors should not occur
on an ADD). The following statement will round WS-DECIMAL2 to its specified decimal
places.
Format 2: Giving
Here the operand or operands to the left of the "TO" together with the one operand to
the right of the "TO" are added together, and then the operand or operands after the
GIVING are updated by that amount.
Again, ROUNDED after a receiving field rounds the answer. The next statement uses
ROUNDED.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE82.
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA 5 TIMES.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A SIMPLE ADDITION TEST".
INITIALIZE WS-DATA-ITEMS.
MOVE 12.2 TO WS-WEEKLY-SALES
RESULT-1
RESULT-2
MOVE 2.45 TO WS-DECIMAL-1
MOVE 10 TO WS-WEEK1
MOVE 3 TO WS-WEEK2
MOVE 3 TO WS-WEEK3
MOVE 2 TO WS-WEEK4
.
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
Format 1: Simple
ADD WS-WEEKLY-SALES TO WS-MONTHLY-SALES
DISPLAY "WS-WEEKLY-SALES = " WS-WEEKLY-SALES
DISPLAY "WS-MONTHLY-SALES = "
WS-MONTHLY-SALES
DISPLAY " "
Rounding
ADD 123.456 TO 654.321
GIVING WS-ANSWER ROUNDED.
DISPLAY "WS-ANSWER = " WS-ANSWER
DISPLAY " "
DISPLAY " ".
This verb also has two formats, again with and without GIVING.
Format 1: Simple
The value in the field or fields to the left of the "FROM" are added together if
appropriate. That total value is subtracted from the field or fields to the right of the
"FROM."
Format 2: Giving
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE83.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
FILE-CONTROL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DATA-ITEMS.
03 WS-COUNT-1 PIC 9(6).
03 WS-COUNT-2 PIC 9(6).
03 WS-SALARY PIC 9(6).
03 TAXES-1 PIC 9(6).
03 TAXES-2 PIC 9(6).
03 WS-COUNT-3 PIC 9(6).
03 WS-GROSS-SALARY PIC 9(6)V9(2).
03 WS-NETT-SALARY PIC 9(6)V9(2).
03 WS-NUM PIC 9(2).
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA 5 TIMES.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A SIMPLE SUBTRACT TEST".
SUBTRACT TAXES-1
TAXES-2 FROM WS-SALARY.
SUBTRACT TAXES-1
TAXES-2
FROM WS-GROSS-SALARY
GIVING WS-NETT-SALARY.
Here the operand or operands to the left of the "FROM" are added together, and their
combined value is subtracted from the one operand after the "FROM." The result is
moved to the field or fields after the GIVING.
Again, the word ROUNDED after a receiving field ensures that rounding takes place.
NOTE: Ensure that all fields from which you are SUBTRACTing are signed fields. If not,
results that should be negative remain positive with potentially catastrophic results.
This verb has the same two formats as ADD and SUBTRACT.
Format 1: Simple
In this format, the value of the operand (field or literal) before the "BY" is multiplied by
the values in the field or fields after the "BY," and the result is moved there.
Why not? Format 1 stores the results of the multiply in the operand after the BY. Also,
1.01 is a literal and cannot be used to store data.
As shown in the next example, you can also include multiple fields after the "BY" so
that the operand before the BY is multiplied by the fields after the "BY."
Format 2: Giving
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE84.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
*Format 2: Giving
*The following example uses the MULTIPLY
*verb with GIVING.
MULTIPLY WS-INTEREST-RATE
MULTIPLY EMPLOYEE-SALARY
BY 1.01 GIVING EMPLOYEE-SALARY.
In this format, only one field or literal can be before the "BY" and one after; however,
the result can be moved to one or more fields (which can include a field already used in
the MULTIPLY).
The word ROUNDED can be inserted after the name of a receiving field.
As a general rule, a MULTIPLY statement is more likely than an ADD statement to result
in a field not being large enough for the calculation result. Trap this out-of-range
condition by using an ON SIZE ERROR clause, discussed later in this chapter.
The DIVIDE statement has a total of five formats. In all cases ROUNDED can be used
after any receiving operand.
The following example uses the DIVIDE verb with INTO and without GIVING.
This results in WS-TOTAL being divided by WS-COUNT. There can be only one operand
(data item or literal) before the INTO, but there may be many operands after the INTO.
Each operand will have the same division carried out on it.
The following example uses the DIVIDE verb with INTO and with GIVING.
This time the result goes into the field or fields after the GIVING.
This format reverses the operands and uses DIVIDE…BY, as shown in the next example.
This yields the same result as the Format 2 statement above. Again multiple receiving
fields can exist after the GIVING.
Formats 4 and 5 add a REMAINDER clause. The following example uses the DIVIDE verb
with INTO without GIVING.
The following example uses the DIVIDE verb with BY with GIVING also yielding a
remainder.
IDENTIFICATION DIVISION.
In summary, we can format the four verbs for arithmetic operations as follows.
Additionally, each verb can use the GIVING format, such as the following:
The COMPUTE statement has a completely different format than the other arithmetic
statements. It uses the standard operators +, -, / (divide), *(multiply), and **
(exponentiation, for example 2**3 represents 2 to the 3rd power), as shown in the
next example.
COMPUTE WS-TOTAL =
(WS-NET-PAY + (WS-OVERTIME * WS-OT-HRS))
* (100 -WS-TAX-RATE)
More than one field name can be placed after the COMPUTE and before the equal sign.
COMPUTE is a comparatively inefficient verb. So it is best to use the other verbs for
simple calculations. For example, ADD 1 TO TOTAL is better than COMPUTE TOTAL =
TOTAL + 1.
Note: An intermediate working storage field may be necessary after the COMPUTE
clause for certain mainframe COBOL dialects.
Order of Evaluation
The order in which items are evaluated makes a difference to the result of the
expression.
In practice, use brackets, wherever possible, to clarify order to you and anyone else
who may have to amend your code later.
The ON SIZE ERROR clause can be used with any arithmetic statement to trap a
numeric calculation going out of range, including division by zero.
The following example shows how to use the ON SIZE ERROR clause.
COMPUTE WS-INVOICE-AMT =
WS-ITEM-PRICE * WS-QUANTITY
ON SIZE ERROR
DISPLAY "ERROR – TOTAL IS TOO LARGE".
In the above example the program will continue if the calculation is within range.
However, if the WS-INVOICE-AMT becomes larger than its PICTURE clause allows, ON
SIZE ERROR displays the error message.
Alternatively, use the NOT ON SIZE ERROR clause as well. This would be necessary if the
COMPUTE statement were within the scope of another conditional statement such as
an IF statement.
The following example uses both the ON SIZE ERROR and the NOT ON SIZE ERROR
clauses.
COMPUTE WS-INVOICE-AMT =
WS-ITEM-PRICE * WS-QUANTITY
ON SIZE ERROR
PERFORM ERROR-ACTIONS
NOT ON SIZE ERROR
PERFORM SET-UP-INVOICE
END-COMPUTE
Example 8.6
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE86.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 WS-DATA-ITEMS.
03 WS-NET-PAY PIC 9(6)V9(2).
03 WS-OVERTIME PIC 9(6)V9(2).
03 WS-OT-HRS PIC 9(6)V9(2).
03 WS-TAX-RATE PIC 9(6)V9(2).
03 WS-total PIC 9(6)V9(2).
03 total PIC 9(6).
03 WS-INVOICE-AMT PIC 9(1)V9(2).
03 WS-ITEM-PRICE PIC 9(6)V9(2).
03 WS-QUANTITY PIC 9(6).
COMPUTE WS-TOTAL =
(WS-NET-PAY + (WS-OVERTIME * WS-OT-HRS))
* (100 - WS-TAX-RATE)
COMPUTE WS-INVOICE-AMT =
WS-ITEM-PRICE * WS-QUANTITY
END-COMPUTE.
COMPUTE WS-INVOICE-AMT =
WS-ITEM-PRICE * WS-QUANTITY
ON SIZE ERROR
DISPLAY "ERROR - TOTAL IS TOO LARGE"
END-COMPUTE.
COMPUTE WS-INVOICE-AMT =
WS-ITEM-PRICE * WS-QUANTITY
This statement can be used to look at a string of data and optionally to manipulate it.
There are four formats.
This format allows you to look through a string for one or more characters or
combination of characters.
• WS-CNT-1 ends up with the value of 2 (there are two 'A's at the beginning of
the string)
• WS-CNT-2 contains 3.
• WS-CNT-3 contains 14 characters in the "AARDVARK EXTRA" string.
If you wish to use FOR CHARACTERS as well as counting something else, specify the
clause last. If you put it first, the INSPECT will count through the whole string for
characters, and its internal pointer will then be at the end of the string, and any other
TALLYING counts will be set to zero.
INSPECT WS-STRING
REPLACING FIRST "A" BY "B"
ALL "R" BY "S".
INSPECT WS-STRING
TALLYING WS-CNT-1 FOR ALL "A"
REPLACING LEADING "A" BY "B"
ALL "R" BY "S".
This sets WS-STRING to "BBSDVASK EXTSA"; however, WS-CNT-1 will contain 4, as there
were four A's in the string at the time. (The TALLYING happens before the REPLACING.)
Every "R" changes to "Q", every "V" to "U" and every "X" to "W".
WS-STRING will therefore be set to "AAQDUAQK EWTQA".
The BEFORE and AFTER clauses can be used with all four formats of INSPECT. The
clauses enable selection of only part of the string for processing.
The following code uses the BEFORE clause with the INSPECT verb.
INSPECT WS-STRING
TALLYING WS-CNT-1
FOR ALL "A" BEFORE SPACE.
The code inspects the string and sets WS-CNT-1 to 3, totaling the three A's before the
space in the "AARDVARK EXTRA" string.
The following code uses the AFTER clause with the INSPECT verb.
INSPECT WS-STRING
TALLYING WS-CNT-2
FOR ALL "R" AFTER "K".
The code inspects the string and sets WS-CNT-1 to 1, totaling the one "R" after the "K"
in the "AARDVARK EXTRA" string.
The following code counts the characters AFTER the "D", but BEFORE the "T".
INSPECT WS-STRING
TALLYING WS-CNT-3 FOR CHARACTERS
AFTER "D" BEFORE "T".
WS-CNT-3 becomes 7.
Example 8.7
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE87.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 WS-CNTS.
03 WS-CNT-1 PIC 99.
03 WS-CNT-2 PIC 99.
03 WS-CNT-3 PIC 99.
INIT-PARA SECTION.
DISPLAY "A SIMPLE INSPECT VERB".
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
INITIALIZE WS-CNTS
MOVE "AARDVARK EXTRA" TO WS-STRING
INSPECT WS-STRING
TALLYING WS-CNT-1 FOR LEADING "A"
WS-CNT-2 FOR ALL "R"
WS-CNT-3 FOR CHARACTERS.
INITIALIZE WS-CNTS
MOVE "AARDVARK EXTRA" TO WS-STRING
INITIALIZE WS-CNTS
MOVE "AARDVARK EXTRA" TO WS-STRING
INSPECT WS-STRING
TALLYING WS-CNT-1 FOR ALL "A"
REPLACING LEADING "A" BY "B"
ALL "R" BY "S".
INITIALIZE WS-CNTS
MOVE "AARDVARK EXTRA" TO WS-STRING
INSPECT WS-STRING
CONVERTING "RVX" TO "QUW".
INSPECT WS-STRING
TALLYING WS-CNT-1
FOR ALL "A" BEFORE SPACE.
Use the STRING format to build up a field from several other fields.
01 WS-FIELDS.
03 WS-FIRST-NAME PIC X(12)
VALUE "BEOWULF".
03 WS-SURNAME PIC X(20)
VALUE "SHAEFFER".
03 WS-SALARY PIC 9(6)V99.
01 WS-OUTPUT-NAME PIC X(30) VALUE SPACES.
The two-character strings will contain trailing spaces, five in the case of WS-FIRST-
NAME and twelve for WS-SURNAME. It would be useful to have the name complete,
but with just one space between the "F" of the first name and the surname.
STRING achieves this with the help of INSPECT afterwards, as used in the following
code.
STRING
WS-FIRST-NAME DELIMITED BY SPACE
"*" DELIMITED BY SIZE
WS-SURNAME DELIMITED BY SPACE
INTO WS-OUTPUT-NAME
END-STRING.
INSPECT WS-OUTPUT-NAME
REPLACING ALL "*" BY SPACE.
In the example STRING places the first name and surname into WS-OUTPUT-NAME.
Here, "DELIMITED BY SPACE" means to process all the characters in the string until a
space is encountered. So, WS-FIRST-NAME and WS-SURNAME are truncated to the
right length. However, we want a space between the two. We can't specify " ", as
DELIMITED BY SPACE will instantly remove it. Instead, we specify an asterisk or any
other character that will not be found in either part of the name. Because "*" does not
contain a space, the whole thing is moved.
We could also have said DELIMITED BY SIZE for this literal, which means "move the
whole field." INSPECT would then turn that asterisk back into a space.
As you might recall from discussions about the MOVE verb, MOVE spacefills a receiving
field if the value received is too small. Since STRING does not do this, it is best to
initialize the receiving field, and then use POINTER and ON OVERFLOW clauses, as
illustrated in the following code.
After this statement, POINTER contains 17, the number of the next free byte (which is
why we set it to 1 at the beginning). Note: Specifying POINTER in this way requires
setting it to a positive value at the start, or nothing will appear in the output string.
The ON OVERFLOW clause is triggered if the STRING is impossible because the number
of characters being transferred is too large for the receiving field. ON OVERFLOW will
also be triggered if a POINTER clause cannot occur, which would happen if the pointer
field is less than 1 or greater than the number of bytes in the receiving field.
STRING
WS-FIRST-NAME DELIMITED BY SPACE
"*" DELIMITED BY SIZE
WS-SURNAME DELIMITED BY SPACE
INTO WS-OUTPUT-NAME
POINTER WS-POINTER
ON OVERFLOW PERFORM TOO-BIG
NOT ON OVERFLOW PERFORM OK-ACTIONS
END-STRING
Example 8.8
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE88.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-FIELDS.
03 WS-FIRST-NAME PIC X(12).
03 WS-SURNAME PIC X(20).
03 WS-SALARY PIC 9(6)V99.
01 WS-POINTER PIC 9(4).
01 WS-OUTPUT-NAME PIC X(30).
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA
PERFORM END-PARA.
INIT-PARA SECTION.
*Format 1:
INITIALIZE WS-OUTPUT-NAME
WS-POINTER
MOVE "BEOWULF" TO WS-FIRST-NAME
MOVE "SHAEFFER" TO WS-SURNAME
STRING
WS-FIRST-NAME DELIMITED BY SPACE
"*" DELIMITED BY SIZE
WS-SURNAME DELIMITED BY SPACE
INTO WS-OUTPUT-NAME
END-STRING
INSPECT WS-OUTPUT-NAME
REPLACING ALL "*" BY SPACE.
DISPLAY "AFTER INSPECT WS-OUTPUT-NAME = "
WS-OUTPUT-NAME
*Format 2:
INITIALIZE WS-OUTPUT-NAME
MOVE 1 TO WS-POINTER
MOVE "BEOWULF" TO WS-FIRST-NAME
MOVE "SHAEFFER" TO WS-SURNAME
STRING
WS-FIRST-NAME DELIMITED BY SPACE
"*" DELIMITED BY SIZE
WS-SURNAME DELIMITED BY SPACE
INTO WS-OUTPUT-NAME
POINTER WS-POINTER
ON OVERFLOW PERFORM TOO-BIG
END-STRING
INITIALIZE WS-OUTPUT-NAME
MOVE 20 TO WS-POINTER
MOVE "BEOWULF" TO WS-FIRST-NAME
MOVE "SHAEFFER" TO WS-SURNAME
STRING
WS-FIRST-NAME DELIMITED BY SPACE
"*" DELIMITED BY SIZE
WS-SURNAME DELIMITED BY SPACE
*Format 3:
INITIALIZE WS-OUTPUT-NAME
MOVE 1 TO WS-POINTER
MOVE "BEOWULF" TO WS-FIRST-NAME
MOVE "SHAEFFER" TO WS-SURNAME
STRING
WS-FIRST-NAME DELIMITED BY SPACE
"*" DELIMITED BY SIZE
WS-SURNAME DELIMITED BY SPACE
INTO WS-OUTPUT-NAME
POINTER WS-POINTER
ON OVERFLOW PERFORM TOO-BIG
NOT OVERFLOW PERFORM OK-ACTIONS
END-STRING
The UNSTRING format, as you might expect, works in the opposite way to STRING. Use
it to divide up a string of characters. It works by recognizing delimiter characters. So,
even more than with STRING, delimiters help to ensure that the receiving fields contain
the correct information.
After this statement, R-FIELD-1 contains "PATRICIA", R-FIELD-2 is blank (there is nothing
before the next delimiter), and R-FIELD-3 contains "HUMPHREYS".
To UNSTRING a string with more than one contiguous occurrence of a delimiter as just
one character, use the following code.
UNSTRING INPUT-NAME
DELIMITED BY ALL "*"
INTO R-FIELD-1 R-FIELD-2 R-FIELD-3
END-UNSTRING
UNSTRING [field]
DELIMITED BY (ALL) [literal or field name]
OR (ALL) [literal or field name] etc.)
INTO [field name]
(DELIMITER [field name])
(COUNT [field name])
INTO [field name]
(DELIMITER [field name])
(COUNT [field name] etc)
(POINTER [field name])
(TALLYING IN [field name])
(ON OVERFLOW [action(s)])
(NOT ON OVERFLOW [action()s])
(END-UNSTRING)
• UNSTRING can use many different potential delimiters to separate out each
substring.
• In each case, the field specified by DELIMITER holds the actual delimiter used.
• The COUNT field records how many bytes were transferred.
• POINTER keeps track of the total number of bytes moved over. As with STRING,
set it to a positive value before the statement takes place.
• TALLYING IN keeps count of the total number of receiving fields that contain
data when the UNSTRING finishes.
• ON OVERFLOW and NOT ON OVERFLOW are as before.
Reference modification
This code results in six characters, starting at position 12, being copied across. WS-
SMALLER-STRING will therefore contain "LMNOPQ".
Example 8.9
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE89.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 INPUT-NAME PIC X(30).
01 RECEIVING-FIELDS.
03 R-FIELD-1 PIC X(12).
03 R-FIELD-2 PIC X(12).
03 R-FIELD-3 PIC X(12).
01 WS-TALLY PIC 9(4).
01 WS-OUTPUT-NAME PIC X(30).
01 WS-ALPHABET PIC X(26)
01 WS-SMALLER-STRING PIC X(10).
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
PERFORM LOOP-PARA
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "A SIMPLE UNstring TEST".
INIT-PARA-EXIT.
EXIT.
LOOP-PARA SECTION.
*Format 1:
INITIALIZE WS-OUTPUT-NAME
RECEIVING-FIELDS
MOVE "PATRICIA**HUMPHREYS" TO INPUT-NAME
UNSTRING INPUT-NAME
DELIMITED BY "*"
INTO
R-FIELD-1 R-FIELD-2 R-FIELD-3
END-UNSTRING
INITIALIZE WS-OUTPUT-NAME
RECEIVING-FIELDS
UNSTRING INPUT-NAME
DELIMITED BY ALL "*"
INTO R-FIELD-1 R-FIELD-2 R-FIELD-3
END-UNSTRING
*Format 3:
INITIALIZE WS-OUTPUT-NAME
RECEIVING-FIELDS
WS-TALLY
UNSTRING INPUT-NAME
DELIMITED BY ALL "*"
INTO R-FIELD-1 R-FIELD-2 R-FIELD-3
TALLYING IN WS-TALLY
END-UNSTRING
• The input file may have any number of records (including 0).
Strictly speaking, all regions should be shown as separate, but because the actions are
identical for all, it will do no harm to group them together.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE810.
ENVIRONMENT DIVISION .
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION .
FILE-CONTROL .
SELECT INFILE ASSIGN "INFILE810.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
DATA DIVISION .
FD INFILE .
01 INREC .
03 REGION PIC X .
88 NORTH VALUE "N" .
88 SOUTH VALUE "S" .
88 EAST VALUE "E" .
88 WEST VALUE "W" .
03 IN-TOTAL PIC 9(6)V99 .
WORKING-STORAGE SECTION .
01 INFILE-FILE-STATUS PIC X(02).
01 WS-EOF PIC 9.
88 END-OF-FILE VALUE 1 .
01 WS-NUMERICS.
03 WS-N-RECORDS PIC 9(4) .
03 WS-S-RECORDS PIC 9(4) .
03 WS-E-RECORDS PIC 9(4) .
03 WS-W-RECORDS PIC 9(4) .
03 WS-N-TOTAL PIC 9(10)V99 .
03 WS-S-TOTAL PIC 9(10)V99 .
03 WS-E-TOTAL PIC 9(10)V99 .
03 WS-W-TOTAL PIC 9(10)V99 .
03 WS-N-AVERAGE PIC 9(6)V99 .
03 WS-S-AVERAGE PIC 9(6)V99 .
03 WS-E-AVERAGE PIC 9(6)V99 .
03 WS-W-AVERAGE PIC 9(6)V99 .
01 WS-EDITED-FIELDS .
03 WS-EDITED-N PIC Z(5)9.99 .
03 WS-EDITED-S PIC Z(5)9.99 .
03 WS-EDITED-E PIC Z(5)9.99 .
03 WS-EDITED-W PIC Z(5)9.99 .
01 WS-MESSAGE PIC X(80) .
PROCEDURE DIVISION .
PROG-PARA SECTION.
PERFORM INIT-PARA .
PERFORM BOD-PARA .
PERFORM END-PARA .
INIT-PARA SECTION.
DISPLAY "STARTING CALCULATION PROGRAM" .
INITIALIZE INFILE-FILE-STATUS
WS-EOF
WS-NUMERICS
WS-MESSAGE
OPEN INPUT INFILE.
IF INFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID INFILE810 FILE" INFILE-FILE-STATUS
PERFORM READ-FILE.
PROCESS-REC-EXIT.
EXIT.
READ-FILE SECTION.
READ INFILE
AT END SET END-OF-FILE TO TRUE
END-READ.
READ-FILE-EXIT.
EXIT.
• Before calculating any average, the program makes a check to ensure that
records were found for that region. This avoids a "divide by zero" error.
• STRING produces messages, which are stored in WS-MESSAGE and from there
DISPLAYed. Record counts employ normal fields, and so 4 displays as "0004".
However, to show the difference in appearance, edited fields are used for the
8.7. Summary
In this module we have examined the following topics:
The purpose of this program is to read the Sales Master file and accumulate the total
sales for each region and a grand total of all regions. This program should calculate the
average overall sale and average sales for each region
It should also calculate Sales for each region as a percentage (to two decimal places,
rounded) of the total sales. All of the totals and averages should be displayed.
Details
The program created by this exercise can use the program created in the last exercise
as a starting point.
• Read in a file of records, and then deal with them in different ways depending on the type of
record found. The input file may have any number of records, including zero.
• Each record should contain a one-character region ("N", "S", "E" or "W"), and an eight-digit
sales total field (six digits, two decimal places). Any record that does not have a valid region
code should be displayed. Do not take any further action with that record (meaning, do not
include any value in the sales field in the calculations shown below).
• Include four totals, one for each region. As the program reads each record, update the
appropriate total with the value in the sales total.
• At the end of the program, display the following:
• All record counts, including invalid records
• Grand total of sales
• Total sales for each region
• An average overall sale and average sales for each region
• Sales for each region as a percentage (to two decimal places, rounded) of the total sales
5. The displays produced by the program should have the same format as the following: (Note the
6. Save the program in the X:\CLASS\MODULE8\NETX directory and provide the program name
PROGRAM81.CBL.
9.1. Objectives
At the end of this module you will be able to:
• Name
• Salary
• 5 sets of weekly sales figures
This group of data repeats. These could be expressed as shown in the following code.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE91.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 SALES-RECORD.
03 SALES-NAME PIC X(20).
03 SALES-SALARY PIC 9(6)V99.
03 SALES-WEEK-1 PIC 9(5)V99.
03 SALES-WEEK-2 PIC 9(5)V99.
03 SALES-WEEK-3 PIC 9(5)V99.
03 SALES-WEEK-4 PIC 9(5)V99.
03 SALES-WEEK-5 PIC 9(5)V99.
01 MONTHLY-SALES PIC 9(10)V99.
PROCEDURE DIVISION.
MAINLINE SECTION.
PERFORM INIT-PARA.
This is clumsy and would become totally unworkable if it were necessary to store 52
such fields, say one for every week of the year.
We could define the information in the previous code using a table, as follows.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE92.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
Using Subscripts
However, there is a far better way to write this by using a variable as the subscript and
a form of the verb PERFORM, as shown in the following code.
DISPLAY MONTHLY-SALES.
LOOP-PARA-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY "MANY THANKS!".
END-PARA-EXIT.
GOBACK.
The PERFORM verb starts by setting WS-SUB to 1, and as the subscript is not greater
than 5, SALES-WEEK(1) is added to MONTHLY-SALES (which should have been initialized
at some point). The loop goes around again. WS-SUB is incremented automatically (BY
1) and the second occurrence is added. This goes on until SALES-WEEK(5) has been
added. The next time around the UNTIL is now true, so the PERFORM stops.
Although this code takes up as many lines on the page as the explicit adding of all five
occurrences, it is far more logical. Furthermore, it would be very straightforward to
modify both the record and the code to cope with 52 entries, as shown next.
NOTE: When using tables, make sure that the subscript always keeps in range. For
example, when using OCCURS 10, the subscript pointing to the table should never go
below 1 or exceed 10, or it will try and access memory used by something else. At
runtime this could cause the program to crash.
A related error is to use a subscript that is too small. For example, a table that OCCURS
100 must have a subscript PIC 999. It is a common beginner's mistake to make the
subscript PIC 99, which works perfectly until the subscript is 99 and 1 is added. The
subscript then resets to zero. At the very least the program will do something wrong.
Here the number (01-06) could be read in from a file. Next, we want to look through
the table, finding a match. So, if 04 is on the input record, we want to use "Cat Food
Medium" and 16.50, perhaps on an invoice.
This needs both predefined entries (in other words, VALUE clauses) and an occurring
table. However, both cannot exist in the same place. We can use the REDEFINES clause
to initialize the values in a table. Use the REDEFINES in the WORKING STORAGE section.
The following shows how we might define the data using VALUE clauses and then load
the table using a REDEFINES clause.
01 WS-PETFOOD.
03 FILLER PIC X(22)
VALUE "01Dog Food Large 2799".
03 FILLER PIC X(22)
VALUE "02Cat Food Large 2399".
03 FILLER PIC X(22)
VALUE "03Dog Food Medium 1750".
03 FILLER PIC X(22)
VALUE "04Cat Food Medium 1650".
03 FILLER PIC X(22)
VALUE "05Dog Food Small 0999".
03 FILLER PIC X(22)
VALUE "06Cat Food Small 0899".
01 WS-PETFOOD-REDEF REDEFINES WS-PETFOOD.
03 WS-PET-DETAILS OCCURS 6.
05 WS-TABLE-NUM PIC 99.
05 WS-FOOD-DESCRIPTION
PIC X(16).
05 WS-PRICE PIC 99V99.
Here is a complete sample program, showing how the table could be used.
Exercise (Add code to create total and add the result to the file) The answer is
1008.71
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE95.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
PROCEDURE DIVISION.
PROG SECTION.
PERFORM INIT-PARA.
PERFORM BOD-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "PROGRAM STARTING".
INITIALIZE INFILE-FILE-STATUS
WS-EOF
WS-SUB
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO THEN
PERFORM PROCESS-REC
PERFORM READ-FILE
END-PERFORM.
BOD-PARA-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY "FINISHED.".
CLOSE INFILE
OUTFILE.
END-PARA-EXIT.
GOBACK.
PROCESS-REC SECTION.
PERFORM VARYING WS-SUB FROM 1 BY 1
UNTIL WS-SUB > 6
IF WS-TABLE-NUM(WS-SUB)
= IN-PRODUCT-NUM
MOVE WS-FOOD-DESCRIPTION(WS-SUB)
TO OUT-PRODUCT-DESC
MOVE IN-QTY TO OUT-QTY
COMPUTE OUT-PRICE =
OUT-QTY * WS-PRICE(WS-SUB)
WRITE OUTREC
EXIT PERFORM
END-IF
END-PERFORM.
PROCESS-REC-EXIT.
EXIT.
READ-FILE SECTION.
READ INFILE
AT END SET END-OF-FILE TO TRUE
END-READ.
READ-FILE-EXIT.
EXIT.
Let's look at the logic. Each input record contains a product number. A PERFORM loop
goes through the table looking for a match. When the PERFORM loop finds a match,
the corresponding description is copied across to an output record, an invoice amount
is calculated from the quantity (originally from the input record), and the price is
calculated from the table. Once a match has been found, we can use the very useful
• Indexes hold a pointer to each table entry, providing more efficient access. For
example, imagine a table of ten elements, each four bytes long. If the index is
set to 6, the pointer will contain 20, with the last byte of the element before the
present position (the fifth). Setting the index to 10 will internally set the pointer
to 36 (the last byte in the ninth element). Setting it to 1 will make the internal
value 0.
17-20 33-36
1-4 5-8 9-12 13-16 21-24 25-28 29-32 37-40
ê ê
.... .... .... .... .... .... .... .... .... ....
1 2 3 4 5 6 7 8 9 10
• If an indexed table is sorted (as the pet food example was), then it can be
searched very efficiently, using the so-called "binary chop" method. This finds,
for example, any element in a thousand-element table in a maximum of ten
steps.
NOTE: To move a value to an index, do not use the MOVE statement; instead, use SET.
We shall see this later.
To specify that a table should be indexed rather than subscripted, the OCCURS
statement needs to be slightly different, as shown next. Specify the index by using the
INDEXED BY clause after the OCCURS clause. Use a unique data name (for example,
PET-IX) after INDEXED BY.
The index is declared implicitly, so you must not define it elsewhere in working storage.
The ASCENDING (or DESCENDING) KEY clause should only be included if the table is
actually sorted; the clause specifies which field in the table is the one to be searched
against.
Use the SEARCH verb to access the tables. Use SEARCH ALL for sorted tables or SEARCH
for unsorted. SEARCH starts at the beginning of the table and searches to the end,
which is not efficient across large tables.
SEARCH ALL is more efficient because it performs a binary search starting in the middle
determining whether the value is greater or less than the item being searched. This
splitting process continues until the SEARCH ALL is complete. To use SEARCH ALL, the
table must be indexed and the elements of the table must be in ascending or
descending sequence.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE97.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
SELECT INFILE ASSIGN "INFILE97.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
FD OUTFILE.
01 OUTREC.
03 OUT-PRODUCT-DESC PIC X(15).
03 OUT-QTY PIC 99.
03 OUT-PRICE PIC 9(4)V99.
The program triggers the AT END clause when it does not find a match with the SEARCH
ALL (the match must be equal with SEARCH ALL).
There is no need to initialize the index with SEARCH ALL. To move a value to an index,
do not use the MOVE statement; instead, use SET. We shall see this later.
For an unsorted table the ASCENDING clause is not included. as shown below.
SET PET-IX TO 1.
SET PET-IX UP by 2.
One thing you must do with an unsorted SEARCH is to set the index to an initial value,
or the AT END clause will always occur.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE98.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-370.
OBJECT-COMPUTER. IBM-370.
INPUT-OUTPUT SECTION.
SELECT INFILE ASSIGN "INFILE98.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
FD OUTFILE.
01 OUTREC.
03 OUT-PRODUCT-DESC PIC X(15).
03 OUT-QTY PIC 99.
03 OUT-PRICE PIC 9(4)V99.
WORKING-STORAGE SECTION.
01 OUTFILE-FILE-STATUS PIC X(02).
01 INFILE-FILE-STATUS PIC X(02).
01 WS-EOF PIC 9.
88 END-OF-FILE VALUE 1.
01 WS-PETFOOD.
03 FILLER PIC X(22)
VALUE "01DOG FOOD LARGE 2799".
03 FILLER PIC X(22)
VALUE "02CAT FOOD LARGE 2399".
03 FILLER PIC X(22)
VALUE "03DOG FOOD MEDIUM 1750".
03 FILLER PIC X(22)
VALUE "04CAT FOOD MEDIUM 1650".
03 FILLER PIC X(22)
VALUE "05DOG FOOD SMALL 0999".
03 FILLER PIC X(22)
PROCEDURE DIVISION.
PROG SECTION.
PERFORM INIT-PARA.
PERFORM BOD-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "PROGRAM STARTING".
INITIALIZE OUTFILE-FILE-STATUS
INFILE-FILE-STATUS
WS-EOF
WS-SUB
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID INFILE98 FILE ="
INFILE-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE.
IF OUTFILE-FILE-STATUS NOT = ZERO
DISPLAY "INVALID OUTFILE98 FILE ="
OUTFILE-FILE-STATUS
GOBACK
END-IF
PERFORM READ-FILE.
INIT-PARA-EXIT.
EXIT.
BOD-PARA SECTION.
PERFORM UNTIL END-OF-FILE
PERFORM PROCESS-REC
PERFORM READ-FILE
END-PERFORM.
BOD-PARA-EXIT.
EXIT.
END-PARA SECTION.
DISPLAY "FINISHED.".
CLOSE INFILE
OUTFILE.
END-PARA-EXIT.
GOBACK.
PROCESS-REC SECTION.
SET PET-IX TO 1
SEARCH WS-PET-DETAILS
AT END DISPLAY "INVALID PRODUCT NUMBER "
IN-PRODUCT-NUM
GOBACK
Always use the SET verb to modify index values, as shown next.
SET PET-IX TO 1.
SET TABLE-IX UP BY 1.
SET TABLE-IX DOWN BY WS-NUM.
MOVE 1 TO WS-SUB.
ADD 1 TO TABLE-SUB.
SUBTRACT WS-NUM FROM TABLE-SUB.
However, you could use SET with subscripts in the same way.
01 WS-SALES-DATA.
03 WS-REGION OCCURS 10.
05 WS-REGION-NAME PIC X(20).
05 WS-SALESPERSON OCCURS 20.
07 WS-PERSONS-NAME PIC X(20).
07 WS-PERSONS-SALES PIC 9(5)V99
OCCURS 52.
Everything can be held under one 01 level. Referencing data items in this sort of table
is not difficult: WS-SALESPERSON will need two subscripts or indexes, as shown next.
The highest level index appears first. Use commas to make the text clearer.
WS-SALESPERSON(4 6)
WS-SALESPERSON(4, 6) [Using commas]
WS-SALESPERSON (4 6) would refer to the 6th person in the fourth
region. WS-PERSONS-SALES will need three subscripts or indexes,
as shown next.
WS-PERSONS-SALES(19 14 1)
This would define the first set of sales figures for the 14th person in region 19.
In both the above examples, numeric literals (numbers) were used to reference the
items. These can be replaced with, or mixed with, data items, as shown in the next
example.
01 OUTREC.
03 OUT-NAME PIC X(20).
03 OUT-ADDRESS.
05 OUT-ADDR-LINE PIC X(20) OCCURS 3.
03 OUT-COMMENT-COUNT PIC 999.
03 OUT-COMMENTS.
05 OUT-COMMENT-BYTE PIC X
OCCURS 0 TO 100
DEPENDING ON OUT-COMMENT-COUNT.
Here an output record contains a fixed 83 bytes, with a further field OUT-COMMENTS
that may be up to 100 bytes long or may even be missing completely. This gives us a
variable-length record file.
If the program OPENs a variable-length record file for INPUT, the program reserves the
maximum possible space in the record buffer, since there is no way of knowing in
advance how big the variable portion will be.
01 CUSTOMER-RECORD.
03 CUSTOMER-NO PIC 9(6).
03 CUSTOMER-BALANCE PIC S9(8)V99.
03 CUSTOMER-INVOICE-CNT
PIC 999.
03 CUSTOMER-INVOICE OCCURS 1 TO 500
DEPENDING ON CUSTOMER-INVOICE-CNT.
05 INVOICE-NO PIC 9(6).
05 INVOICE-DATE PIC 9(8).
05 INVOICE-AMT PIC 9(6)V99.
The program that produces a new customer invoice would use code similar to the
following. The program increments the current value of CUSTOMER-INVOICE-CNT,
which becomes the subscript for the next invoice.
PERFORM CALCULATE-INVOICE-AMT.
PERFORM CALCULATE-INVOICE-NO.
PERFORM GET-DATE.
ADD 1 TO CUSTOMER-INVOICE-CNT.
MOVE WS-INVOICE-NO
TO INVOICE-NO(CUSTOMER-INVOICE-CNT).
MOVE WS-TODAYS-DATE
TO INVOICE-DATE(CUSTOMER-INVOICE-CNT).
MOVE WS-INVOICE-AMT
TO INVOICE-AMT(CUSTOMER-INVOICE-CNT).
When using the OCCURS DEPENDING clause, the following rules apply.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE99.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
SELECT INFILE ASSIGN "INFILE99.DAT"
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS INFILE-FILE-STATUS.
PROCEDURE DIVISION.
PROG SECTION.
PERFORM INIT-PARA.
PERFORM BOD-PARA.
PERFORM END-PARA.
INIT-PARA SECTION.
DISPLAY "TABLE HANDLING PROGRAM STARTING" .
INITIALIZE OUTFILE-FILE-STATUS
INFILE-FILE-STATUS
WS-EOF
WS-VALID-COUNTS
WS-SALES-SUB
WS-INVALID-COUNT
WS-INVALID-SUB
WS-INVALID-RECS.
OPEN INPUT INFILE
IF INFILE-FILE-STATUS NOT = ZERO THEN
DISPLAY "INVALID INFILE99 FILE = "
INFILE-FILE-STATUS
GOBACK
END-IF
OPEN OUTPUT OUTFILE.
IF OUTFILE-FILE-STATUS NOT = ZERO THEN
DISPLAY "INVALID INFILE99 FILE = "
OUTFILE-FILE-STATUS
GOBACK
END-IF
PERFORM READ-FILE.
INIT-PARA-EXIT.
EXIT.
BOD-PARA SECTION.
PERFORM UNTIL NO-MORE-RECORDS
PERFORM PROCESS-REC
PERFORM READ-FILE
END-PERFORM.
BOD-PARA-EXIT.
EXIT.
END-PARA SECTION.
CLOSE INFILE
OUTFILE .
PROCESS-REC SECTION.
IF VALID-TYPE AND IN-NAME UNEQUAL SPACES THEN
PERFORM VALID-RECORD
ELSE
PERFORM INVALID-RECORD
END-IF.
RECORD-BOD-EXIT.
EXIT.
VALID-RECORD SECTION.
EVALUATE TRUE
WHEN A-C
ADD 1 TO WS-A-C-COUNT
WHEN B-E-T
ADD 1 TO WS-B-E-T-COUNT
WHEN J
ADD 1 TO WS-J-COUNT
WHEN OTHER
CONTINUE
END-EVALUATE
MOVE IN-TYPE TO OUT-TYPE .
MOVE IN-NAME TO OUT-NAME .
MOVE IN-START-DATE TO OUT-START-DATE .
MOVE 0 TO OUT-SALES
• Firstly, the input records contain twelve occurrences of IN-SALES, which are
totalled up into OUT-SALES using a PERFORM with a subscript – a subscript
loop. (The output field is used as a total. An alternative would have been to use
a working-storage field and move it.)
• The second use is the storing of invalid records. The specification decrees that
any such records should display at the end of the program, rather than when
they are encountered. The solution is to have an OCCURS 100, which will hold
that many invalid records. As the program finds an invalid record, the program
increments a subscript incremented and stores the record details. At the end of
the program the subscript is checked, and if non-zero, the records display. The
subscript value is passed to another count, so that a PERFORM VARYING loop
can be used from 1, until the correct number of records is processed.
The danger inherent in storing records in this way is that more may appear than
anticipated. If the subscript goes above 100, the program terminates. Another
approach would be to ignore every invalid record above the limit that can be stored
and set a flag showing that this has been done. At program end the first 100 records
could display along with a message indicating that only these records were processed.
9.9. Summary
In this module we have examined the following topics:
The purpose of this program is to read the Sales Master file and for all valid records,
total all twelve sales fields together and store them in a table.
At the end of the program display all valid records (in the order in which they were
found).
Details
The program created by this exercise can use the program created in previous exercise
as a starting point.
4. Save the program in the X:\CLASS\NETMODULE9\NETX directory and provide the program name
PROGRAM91.CBL.