I would like to write a function in COBOL that converts characters from a PIC X(60) data type to uppercase. While I know one way of doing this, I would like to be able to manually go through the letters and add a constant to go from lower case to upper case, but I cannot seem to be able to perform any arithmetic on PIC X data types, unless I redefine them as something else, but then I seem to corrupt the original X character.
Can someone give me an example of how, given a PIC X variable, you can increment it by 1 letter (i.e. a -> b, d -> e... etc). In other words, I would like to be able to add 1 to this variable:
01 char-temp pic x.
Thanks,
Ivan
It is usually a very bad idea to do case folding this way, it really only works for 7-bit ASCII. What you ask is possible, and even easy to do, but I'll mention some other ways to uppercase your data first.
Easiest way:
Move function upper-case(my-src-field) To my-tgt-field
Very easy way:
Inspect my-field
converting 'abc...z' to 'ABC...Z'
End-Inspect
Hard way, to add one to 01 char-temp pic x:
01 my-working-storage.
02 char-temp-area.
03 filler pic x.
03 char-temp pic x.
02 char-temp-9 redefines char-temp-area pic s9(4) comp
.
Then you can:
Move 'A' to char-temp
Add +1 to char-temp-9
And char-temp will contain a 'B'. As others have mentioned, Cobol is not nice about single byte binary math, so you have to make your character fit in the low byte of a binary number, in this case a 2 byte binary number.
You could be naughty and use redefines eg:
01 char-temp pic x.
01 char-temp9 pic 99 comp-x redefines char-temp.
move 65 to char-temp9
display char-temp
Or use reference modification:
01 char-temp pic x.
01 char-temp9 pic 99 comp-x.
move 65 to char-temp9
move char-temp9(1:length of char-temp9) to char-temp
display char-temp
and I am sure they are other approaches too...
A single character generally takes one byte of memory. Standard COBOL does not have a binary numeric data type of one byte (Note: PIC 9 USAGE DISPLAY is a character representation of a digit not the binary representation of the digit). So, doing binary arithemetic on single characters in COBOL isn't going to work for you.
Even if you could perform arithementic on characters, you may be making a bad assumption about the binary representation of characters - the letter 'b' is not necessarily equal to the binary representation of 'a' plus 1 (it might work for ASCII but not for EBCDIC).
Finally, if you want to replace one character by another you should investigate the COBOL INSPECT statement. Unlike bit twiddling, INSPECT is completely portable.
Can someone give me an example of how, given a PIC X variable, you can
increment it by 1 letter (i.e. a -> b, d -> e... etc). In other words,
I would like to be able to add 1 to this variable:
01 char-temp pic x.
For COBOL 85, intrinsic functions CHAR and ORD may be used.
program-id. add1.
data division.
working-storage section.
1 char-temp pic x.
procedure division.
begin.
move "a" to char-temp
display char-temp "->" with no advancing
perform add-1-to-char
display char-temp
stop run
.
add-1-to-char.
move function char (function ord (char-temp) + 1)
to char-temp
.
end program add1.
Result:
a->b
Related
I was practicing array processing and table handling and there's this output of my program that I don't understand.
01 TABLE-VALUES.
05 TABLE-ELEMENTS OCCURS 2 TIMES.
10 A-A PIC X(5).
10 A-B PIC S9(5)V99 COMP-3.
01 WS-STRING PIC X(10).
01 S PIC 99.
01 WS-COUNT PIC 99.
...
PROCEDURE DIVISION.
0000-MAIN.
DISPLAY 'HELLO WORLD'
MOVE '1234567890ABCDEFGHI' TO TABLE-VALUES
DISPLAY 'TABLE VALUES ' TABLE-VALUES
MOVE 0 TO WS-COUNT
INSPECT TABLE-VALUES TALLYING WS-COUNT FOR CHARACTERS
DISPLAY WS-COUNT
PERFORM 1000-PARA VARYING S FROM 1 BY 1 UNTIL S > 2
STOP RUN.
1000-PARA.
MOVE 'A-A(&&) = ' TO WS-STRING
INSPECT WS-STRING REPLACING FIRST '&&' BY S
DISPLAY WS-STRING A-A(S)
MOVE 'A-B(&&) = ' TO WS-STRING
INSPECT WS-STRING REPLACING FIRST '&&' BY S
DISPLAY WS-STRING A-B(S).
The output turned out to be:
HELLO WORLD
TABLE VALUES 1234567890ABCDEFGHI
18
A-A(01) = 12345
A-B(01) = 6 7 8
A-A(02) = 0ABCD
A-B(02) = 5 6 7
I don't understand how A-B(1) and A-B(2) turned out like that. Why are there spaces in between? Where did digit 9 go to?
Try removing the COMP-3 from the A-B definition, it should work better.
Cobol comp-3
Comp-3 is cobol's binary coded decimal format. Each 4 bytes (1/2 byte or nyble) represents a decimal digit with the sign held in the last digit. Moving a character string to a comp-3 value (like you do) will result in a invalid comp-3 value.
Normally the value 1234 would be stored as
`01234C`x
In your case (if using an EBCDIC machine) you are moving 6789 hex string 'f6f7f8f9'x
to the variable A-B(01). The F's are not valid decimal digits and 9 is not a valid comp-3 sign.
Move to Table Explanation
Explanation of
MOVE '1234567890ABCDEFGHI' TO TABLE-VALUES
in the above TABLE-VALUES is treated as a pic x(18). The move completely ignores the definitions of A-A and A-B.
Assuming a definition of
01 TABLE-VALUES.
05 TABLE-ELEMENTS OCCURS 2 TIMES.
10 A-A PIC X(5).
10 A-B PIC S9(5)V99.
The following would make more sense
MOVE 'A-A 100234567A-A 200234567' TO TABLE-VALUES
When doing a move to a group level, The string has to exactly match the
map of the fields in the Group.
For the above Cobol Layout, the fields are aligned like
Field Position Length
A-A(1) 1 5
A-B(1) 6 7
A-A(2) 13 5
A-B(2) 18 7
I don’t know which compiler and directives you are using, but I would have expected the original DISPLAY of the comp-3 usage to ABEND with a numeric exception.
As for the follow up question about the letters in the numeric field, you valued the field by moving a literal to a group item, TABLE-VALUES. Group items always are USAGE ALPHANUMERIC.
According to the COBOL code below when I try to sum WS-NUM1 with WS-NUM2, COBOL seems to supress the last number. For example: variable WS-NUM1 and WS-NUM2 are 10.15, I get 20.20 as result but expected 20.30. What's wrong?
WS-NUM1 PIC 9(2)V99.
WS-NUM2 PIC 9(2)V99.
WS-RESULTADO PIC 9(2)V99.
DISPLAY "Enter the first number:"
ACCEPT WS-NUM1.
DISPLAY "Enter the second number:"
ACCEPT WS-NUM2.
COMPUTE WS-RESULTADO = WS-NUM1 + WS-NUM2.
Thanks in advance.
PIC 9(2)v99 defines a variable with an implied decimal place not a real one. You're trying to enter data containing a decimal point and it's not working because you have to strip out the '.' to get the numeric part of your data to properly fit in the 4 bytes that your working storage area occupies.
PROGRAM-ID. ADD2.
data division.
working-storage section.
01 ws-num-input pic x(5).
01 WS-NUM1 PIC 9(2)V99 value 0.
01 redefines ws-num1.
05 ws-high-num pic 99.
05 ws-low-num pic 99.
01 WS-NUM2 PIC 9(2)V99 value 0.
01 redefines ws-num2.
05 ws-high-num2 pic 99.
05 ws-low-num2 pic 99.
01 WS-RESULTADO PIC 9(2)V99.
PROCEDURE DIVISION.
DISPLAY "Enter the first number:"
*
accept ws-num-input
unstring ws-num-input delimited by '.'
into ws-high-num, ws-low-num
DISPLAY "Enter the second number:"
accept ws-num-input
unstring ws-num-input delimited by '.'
into ws-high-num2, ws-low-num2
*
COMPUTE WS-RESULTADO = WS-NUM1 + WS-NUM2.
DISPLAY WS-RESULTADO
STOP RUN
.
This is just a simple demonstration. In a real world application you would have to insure much more robust edits to ensure that valid numeric data was entered.
If I declare it like this
01 WS-NUM1 PIC 9(2)V99.
01 WS-NUM2 PIC 9(2)V99.
01 WS-RESULTADO PIC 9(2)V99.
and define and sum them up like this
SET WS-NUM1 TO 10.15.
SET WS-NUM2 TO 10.15.
COMPUTE WS-RESULTADO = WS-NUM1 + WS-NUM2.
DISPLAY WS-RESULTADO.
I get the expected result of 20.30.
This looks like a job for a special type of PICture : Edited picture
Indeed you seem to know about the vanilla PICture clause (I'm writing PICture because as you may know it you can either write PIC or PICTURE).
A vanilla number PIC contains only 4 different symbols (and the parentheses and numbers in order to repeat some of the symbols)
9 : Represents a digit. You can repeat by using a number between parentheses like said before.
S : Means that the number is signed
V : Show the position of the implicit decimal point
P : I've been told that it exists but I honestly never found it in the codebase of my workplace. Its another kind of decimal point used for scaling factors but I don't know much about it.
But there are other symbols.
If you use theses other mysterious symbols the numeric PIC becomes an edited numeric PIC. As its name says, an edited PICture is made to be shown. It will allow you to format your numbers for better presentation or to receive number formatted for human reading.
Once edited, you cannot use it to make computations so you will have to transfer from edited to vanilla to perform computations on the latter. And you move from vanilla to edited in order to display your results.
So now I shall reveal some of these mysterious symbols :
B : Insert a blank at the place it is put
0 : Insert a zero at the place it is put
. : Insert the decimal point at the place it is put
: Insert a + if the number is positive and a - if the number is negative
Z : Acts like a 9 if the digits it represents has a value different than 0. Acts like a blank if the digits has the value of 0.
To my knowledge there are also : / , CR DB * $ -
You can look up for it on the internet. They really show the accountant essence of cobol.
For your problems we are really interested by the "." which will allow us to take into account the decimal point you have the write when you type down your input.
For a bonus I will also use Z which will make your result looks like 2.37 instead of 02.37 if the number is less than ten.
Note that you cannot use the repeating pattern with parenthesis ( 9(03) for instance) when describing an edited picture ! Each digits has to represented explicitly
IDENTIFICATION DIVISION.
PROGRAM-ID. EDITCOMP.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUM1-EDITED PIC 99.99.
01 WS-NUM2-EDITED PIC 99.99.
01 WS-NUM1-CALC PIC 9(2)V99.
01 WS-NUM2-CALC PIC 9(2)V99.
01 WS-RESULTADO-EDITED PIC Z9.99.
PROCEDURE DIVISION.
ACCEPT WS-NUM1-EDITED.
ACCEPT WS-NUM2-EDITED.
MOVE WS-NUM1-EDITED TO WS-NUM1-CALC.
MOVE WS-NUM2-EDITED TO WS-NUM2-CALC.
COMPUTE WS-RESULTADO-EDITED = WS-NUM1-CALC + WS-NUM2-CALC.
DISPLAY WS-RESULTADO-EDITED.
STOP RUN.
You should note that there also exist edited alphanumeric picture. You can insert Blank (B), zeroes (0) or / (/) in it.
I am trying to accept input from jcl for example 'John Snow' and run it from my cobol program Im using JUSTIFIED RIGHT VALUE SPACES to move the string to the right side however I need to delete the extra spaces using my cobol pgm.
example
my working storage is:
01 ALPHA-ITEM PIC X(50).
01 MOVE-ITEM REDEFINES ALPHA-ITEM PIC X(50).
01 NUM-ITEM PIC X(50) JUSTIFIED RIGHT VALUE SPACES.
and in my PROCEDURE DIVISION
ACCEPT ALPHA-ITEM.
MOVE MOVE-ITEM TO NUM-ITEM.
DISPLAY NUM-ITEM.
it displays 'John Snow' on the right of the screen however i don't know how to remove the extra spaces.
you need something like this:
01 ALPHA-ITEM PIC X(50).
01 WS-INDEX PIC 99.
ACCEPT ALPHA-ITEM
PERFORM VARYING WS-INDEX
FROM 50 BY -1
UNTIL ALPHA-ITEM(WS-INDEX:1) NOT EQUAL SPACE
OR WS-INDEX < 1
END-PERFORM
DISPLAY ALPHA-ITEM(1:WS-INDEX).
This code will accept the alpha item, then run a loop to find out how long the data actually is. Then it will display that field starting from position 1 until the counter that was set in the loop.
There is also.. Unpopular for some reason.
UNSTRING MOVE-ITEM DELIMITED BY SPACES INTO NUM-ITEM.
I have a field pic X(03) with a date in it as X'160101' format yymmdd.
I will like to know how to convert it to pic x(06).
So far I tried to move it back to a 9(03) comp and move the
9(03) comp to a 9(06) but it didn't work.
How can I do this?
What you have is a Binary Coded Decimal (BCD). That is, the data is held in a binary field, but it is the decimal representation, not the binary, which is important for the value. X'160101' in would be 1,442,049. X'160201' would be 1,442,305. So if you were able to treat it as a binary field (you'd have to prepend a binary zero to make it the correct length for a binary field) you'd have to do some silly calculation.
So you do something different, and easier. BCD is not a native COBOL datatype. PACKED-DECIMAL (often the same as COMP-3/COMPUTATIONAL-3) is a BCD-type which includes a sign-value in the low-order (right-most) half-byte. So not quite a BCD, but you can treat your BCD as PACKED-DECIMAL like this:
01 BCD-TO-PACKED.
05 BTP-SOURCE-BCD PIC XXX.
05 BTP-PACKED-ZERO PACKED-DECIMAL PIC 9 VALUE ZERO.
01 FILLER
REDEFINES BCD-TO-PACKED.
05 BTP-PACKED-TO-MOVE
PAKCED-DECIMAL PIC 9(6)V9.
01 DATE-AS-PIC-X PIC X(6).
01 DATA-AS-CHARACTER-NUMERIC
REDEFINES DATE-AS-PIC-X PIC 9(6).
MOVE your-source-value TO BTP-SOURCE-BCD
MOVE BTP-PACKED-TO-MOVE TO DATA-AS-CHARACTER-NUMERIC
After that you can happily reference DATE-AS-PIC-X to do whatever you want with it.
BCD-TO-PACKED is a four-byte group field which, through the VALUE clause and through nothing ever changing, has, in the last by X'0F'. That's an unsigned packed-decimal field with one digit and a value of zero.
Then you do the X-to-X MOVE for BTP-SOURCE-BCD. BCD-TO-PACKED now looks like this: X'1601010F'. Which is perfectly valid for a packed-decimal field. You could divide that by 10 to get your date (REDEFINES it as PACKED-DECIMAL PIC 9(7)) but why waste CPU?
Instead, in the REDEFINES you define one decimal place (V9). When the compiler generates the code to MOVE that to another numeric field with no decimal places, the source decimal place(s) are just dropped off. Presto! Divide by 10 without dividing.
Note: The data-names chosen are to aid the explanation. You should make yours meaningful to the data, not just use "trite" names. "Character numeric" is called USAGE DISPLAY in COBOL, it is the default if a USAGE is not supplied. The word USAGE is rarely used itself. So USAGE DISPLAY is what you have when there is a PIC X or PIC 9 field with no other usage.
Note: You should probably not be using two-digit years. You won't be able to deal with dates earlier than 2000 or later than 2199. Historical use of two-digit years was due to expensive disk storage and "data redundancy" (all century values were 19, so why hold the value 19 80 different times for the same account). If using two-digit years, ensure that there is something somewhere which is relevant to the century.
Note: If you attempt to MOVE a PIC X field to a numeric field, the compiler will assume that you have valid character numbers in the field, which is one reason your original attempt failed.
If you search stackoverflow, you should find the answer (this has already been answered).
But Create a fields like (my-date-x holds the date):
03 my-date-x pic x(3).
03 my-date-9 pic 9(6).
03 date-ymdv0 pic 9(6)v9 comp-3.
03 date-x pic x(3) redefines date-ymdv0.
And the code is
Move 0 to date-ymdv0
Move my-date-x to date-x
Move date-ymdv0 to my-date-9
The reason it works is for 9(6)v9 comp3 160101 is stored as x'1601010c'
which is what you have (+ a 0c at the end).
I find myself sorting an input file, and using a control break to compute some data. We need headers in the control break, the report writer is duplicating the header each time and I can not figure it out for the life of me. The write statement in the break paragraph is written twice, but if I use a DISPLAY it is only displayed once. Where am I going wrong with the Report Writer? The break itself is calculating the data correctly (but probably terribly)
environment division.
configuration section.
input-output section.
file-control.
SELECT corpranks
ASSIGN TO
"corpranks.txt"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT out-file
ASSIGN TO
"report"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT sortfile
ASSIGN TO
"SortFile".
data division.
file section.
FD corpranks
RECORD CONTAINS 80 CHARACTERS.
01 gf-rec.
05 first-initial PIC x.
05 middle-initial PIC x.
05 last-name PIC x(14).
05 rank-code PIC 9.
05 Filler PIC x(15).
05 rank PIC x(3).
05 salary PIC 9(6).
05 corporation PIC x(29) VALUE SPACE.
FD out-file
REPORT IS corp-report.
01 of-rec PIC x(80).
SD sortfile.
01 Sortrec.
05 PIC x(16).
05 SR-rank PIC xxx.
05 PIC x(22).
05 SR-corporation PIC x(29).
working-storage section.
77 EOF PIC x VALUE "N".
77 current-corp PIC x(29).
77 total-salary PIC 9(6) VALUE 0.
77 current-salary PIC 9(6).
77 converted-month PIC x(3).
77 concatenated-date PIC x(28).
77 formatted-date PIC x(80) JUSTIFIED RIGHT.
77 formatted-name PIC x(20).
77 tally-counter PIC 9.
77 inp-len PIC 9.
01 current-date.
05 YYYY PIC x(4).
05 MM PIC x(2).
05 DD PIC x(2).
01 corporation-header.
05 FILLER pic x(18) VALUE SPACES.
05 FILLER pic x(13) VALUE "Corporation: ".
05 ch-corp pic x(40).
01 corporation-subheader.
05 FILLER pic x(5) VALUE SPACES.
05 FILLER pic x(4) VALUE "RANK".
05 FILLER pic x(5) VALUE SPACES.
05 FILLER pic x(4) VALUE "NAME".
05 FILLER pic x(15) VALUE SPACES.
05 FILLER pic x(6) VALUE "SALARY".
77 csh-underline pic x(40) Value
"========================================".
01 main-header.
05 FILLER PIC x(5).
05 header-content PIC x(69) VALUE "Jacksonville Computer App
"lications Support Personnel Salaries".
report section.
RD corp-report.
01 REPORT-LINE
TYPE DETAIL
LINE PLUS 2.
05 COLUMN 6 PIC x(3) SOURCE rank.
05 COLUMN 12 PIC x(20) SOURCE formatted-name.
05 COLUMN 37 PIC 9(6) SOURCE salary.
procedure division.
0000-MAIN.
Sort Sortfile on ascending key SR-corporation
on ascending key SR-rank
Using corpranks
giving corpranks.
OPEN
INPUT corpranks
OUTPUT out-file
INITIATE corp-report.
WRITE of-rec FROM main-header.
ACCEPT current-date from DATE YYYYMMDD.
PERFORM 3000-CONVERT-MONTH.
STRING "As of: " DELIMITED BY SIZE
DD DELIMITED BY SIZE
SPACE
converted-month DELIMITED BY SIZE
SPACE
YYYY DELIMITED BY SIZE
INTO concatenated-date.
MOVE concatenated-date TO formatted-date.
WRITE of-rec FROM formatted-date.
PERFORM 2000-GENERATE-REPORT UNTIL EOF = 1.
TERMINATE corp-report.
stop run.
2000-GENERATE-REPORT.
PERFORM 3100-TRIM-FIELDS
GENERATE REPORT-LINE
READ corpranks
AT END
CLOSE corpranks
out-file
MOVE 1 TO eof
NOT AT END
IF current-corp = SPACE
MOVE corporation to current-corp
MOVE current-corp to ch-corp
WRITE of-rec FROM corporation-header
WRITE of-rec FROM corporation-subheader
WRITE of-rec FROM csh-underline
END-IF
IF current-corp NOT = corporation
PERFORM 2500-CONTROL-BREAK
END-IF
COMPUTE total-salary = total-salary + salary
MOVE corporation to current-corp
END-READ.
2500-CONTROL-BREAK.
WRITE of-rec FROM corporation
MOVE 0 to total-salary
.
3000-CONVERT-MONTH.
EVALUATE mm
WHEN "01" MOVE "JAN" TO converted-month
WHEN "02" MOVE "FEB" TO converted-month
WHEN "03" MOVE "MAR" TO converted-month
WHEN "04" MOVE "APR" TO converted-month
WHEN "05" MOVE "MAY" TO converted-month
WHEN "06" MOVE "JUN" TO converted-month
WHEN "07" MOVE "JUL" TO converted-month
WHEN "08" MOVE "AUG" TO converted-month
WHEN "09" MOVE "SEP" TO converted-month
WHEN "10" MOVE "OCT" TO converted-month
WHEN "11" MOVE "NOV" TO converted-month
WHEN "12" MOVE "DEC" TO converted-month
WHEN OTHER MOVE mm to converted-month
END-EVALUATE.
3100-TRIM-FIELDS.
INSPECT last-name TALLYING tally-counter FOR trailing
spaces.
COMPUTE inp-len = LENGTH OF last-name - tally-counter
MOVE last-name(1: inp-len) to formatted-name
STRING last-name(1: inp-len) DELIMITED BY SIZE
SPACE
first-initial DELIMITED BY SIZE
INTO formatted-name
MOVE 0 TO tally-counter
end program Program2.
Some report output: (at the beginning header, csh-underline is the last thing written, the === underline displays twice. At the corporation control breaks, the next corp name is the last thing written, and is written twice)
Jacksonville Computer Applications Support Personnel Salaries
As of: 18 FEB 2015
Corporation: Alltel Information Services
RANK NAME SALARY
========================================
========================================
EVP COLUMBUS C 100000
SVP ADAMS S 042500
VP REAGAN R 081000
VP FRANKLIN B 080000
A&P FORD G 060000
A&P HAYES R 050000
A&P JACKSON A 057600
A&P TYLER J 069000
A&P HARRISON B 052000
A&P TAFT W 070500
A&P HOOVER H 035000
A&P PIERCE F 044000
American Express
American Express
EVP JOHNSON L 098000
SVP CLINTON W 086000
VP ROOSEVELT F 072000
A&P HARDING W 040000
....
Here's a link to some Report Writer documentation from Micro Focus. It is not the only documentation they provide, but it is all that I have scanned through: http://documentation.microfocus.com/help/index.jsp?topic=%2Fcom.microfocus.eclipse.infocenter.studee60win%2FGUID-48E4E734-F1A4-41C4-BA30-38993C8FE100.html
If you loot at Report File under Enterprise > Micro Focus Studio Enterprise Edition 6.0 > General Reference > COBOL Language Reference > Part 3. Additional Topics > Report Writer you will see this:
Report File
A report file is an output file having sequential organization. A
report file has a file description entry containing a REPORT clause.
The content of a report file consists of records that are written
under control of the RWCS.
A report file is named by a file control entry and is described by a
file description entry containing a REPORT clause. A report file is
referred to and accessed by the OPEN, GENERATE, INITIATE, SUPPRESS,
TERMINATE, USE BEFORE REPORTING, and CLOSE statements.
Although this does not definitively say "Don't use your own WRITE statements and hope that they will work" I think it is clear that you should not. What happens when you do that is not defined, or is "undefined behaviour".
You are getting repeated lines before a break, and after a break, exactly where the Report Writer will be checking if there is anything it needs to do. Although I know nothing at all about the implementation of the Report Writer in Micro Focus COBOL, I am pretty certain that you have correctly identified that the repetition happens and is beyond your control. I think the above quote confirms that, and within other parts of Micro Focus's documentation this may be made more explicit.
You either need to use the Report Writer fully (if the task is to use the Report Writer) or not use it at all. You can't mix automatic and manual on the same report file, it seems, and that makes sense to me.
Remember, it does not matter that some of your WRITE statements seem to work, because this is a computer and you need them all to work.
Some general comments on your program:
In main-header you have a FILLER without a VALUE clause, which can cause problems when written to a file for printing. Whether that is way those five bytes don't show on your output or whether it is due to formatting in the posting here, I don't know.
Also in main-header you have a long literal, continued onto a second line. I can't see the continuation marker, and that may be a feature of how it is done in that Micro Focus COBOL, but it always makes things easier if literals are not continued. Define two smaller fields one after the other, with smaller literals which taken together make up the whole.
You have this:
COMPUTE total-salary = total-salary + salary
This, however, is considered clearer:
ADD salary TO total-salary
You are using STRING. You should be aware that the data-transfer from the sending fields ceases when the receiving field is filled, or when all the sending fields have been processed. In the latter case, automatic space-padding is not carried out, unlike the behaviour of a MOVE statement. You need to set your receiving field to an initial value before the STRING is executed, else you will retain data from the previous execution of STRING when the current execution of STRING has less actual data.
After the STRING you do this:
MOVE 0 TO tally-counter
This means your INSPECT, several statements earlier, but where tally-counter is used, is relying on a previous value for tally-counter for the code after that to work. This is not good practice. Make tally-counter an initial value before it is used in the INSPECT.
If you go with the Report Writer your PROCEDURE DIVISION code will be significantly reduced, because the definition of the report elements defines the automatic processing.
The Report Write feature of COBOL is very powerful. It allows you to define a complex report in the REPORT SECTION of a COBOL program, with headings, column headings, detail lines, control-break totals etc. In the PROCEDURE DIVISION you only need as little as make the source-data available (say with a READ) and then GENERATE the report, and COBOL does the rest for you.
However, you have defined a very simple report, and are attempting to do headings, totals etc yourself. I have never done this, and don't know if it works in general, or if it works for your compiler.
From your testing, it seems like there may be a problem with doing this, and it may be, erroneously, repeating the line you yourself have written. You need to check that that particular line is not output elsewhere in your program.
We need to see the outstanding answers to questions from comments, and, unless it is an excessive size, your entire program.
If your exercise is specifically to use the Report Writer, then I think you need to define a more "complex" report, which will produce, automatically from the definition, everything that you want.
If you do not have to use the Report Writer for this exercise, don't use it, just do the detail-line formatting yourself and WRITE it as you are already doing for headings and totals.
On the assumption (later proved false) that you were using the Report Writer to do everything you need, the problem would have been manually writing to the same output file that the Report Writer was using.
If using the full features of the Report Writer, simply make this change and remove any other WRITEs to that output file, and use the Report Writer features for everything:
2500-CONTROL-BREAK.
MOVE 0 to total-salary
.