the format of input file is like this:
Region ******* Company Name
A
B
C
A
C
with many lines.
I need to get a output file to rearrange the file with headers like this:
Company in Region A:
name
name
name...
Company in Region B:
name
name
name..
Company in Region C:
name
name
name..
My question is because the region in input file is not ordered. How can I add the second Region A company back in the header "Company in Region A"? I can only read the file one time (I cannot first all do lines with region A then reopen the file to read again). And I can only have 1 output file.
You could use the Sort verb with input/output procedure to sort the file into Region Sequence.
You can find many examples in Google. This ShorExample has a short Sort example,
there is more info here
You will probably need both input and output procedures
Sort Example:
PROCEDURE DIVISION.
000-SORT SECTION.
010-DO-THE-SORT.
SORT SORT-FILE ON ASCENDING KEY SORT-KEY-1
ON DESCENDING KEY SORT-KEY-2
USING INPUT-FILE
OUTPUT PROCEDURE IS 200-WRITE-OUTPUT
THRU 230-DONE-OUTPUT.
DISPLAY "END OF SORT".
STOP RUN.
200-WRITE-OUTPUT SECTION.
210-OPEN-OUTPUT.
OPEN OUTPUT OUTPUT-FILE.
220-GET-SORTED-RECORDS.
RETURN SORT-FILE AT END
CLOSE OUTPUT-FILE
GO TO 230-DONE-OUTPUT.
MOVE SORT-RECORD TO OUTPUT-RECORD.
WRITE OUTPUT-RECORD.
GO TO 220-GET-SORTED-RECORDS.
230-DONE-OUTPUT SECTION.
240-EXIT-OUTPUT.
EXIT.
If you are doing homework, remember that the goal of homework is not to solve a problem, it is to demonstrate that you have learned the class material. So, if this is homework, create your own solution based on what you have been taught. If you are trying to solve a real-life problem, the example below might help point you in the right direction. If you have not been taught sorting using an output procedure, you do not want to use the example below to do your homework. That said, the following program works using the sample data shown with GNUCobol. Notice in particular how paragraph OUTPUT-CO-BY-REGION-REPORT is used by the SORT.
--- contents of sample data file COMPANY.DAT ---
A WAL-MART
B EXXON
C CHEVRON
B BERKSHIRE
A APPLE
C GENERAL MOTORS
IDENTIFICATION DIVISION.
PROGRAM-ID. COMPANY-BY-REGION.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT COMPANY-FILE
ASSIGN TO 'COMPANY.DAT'
ORGANIZATION IS LINE SEQUENTIAL.
SELECT COMPANY-SORT-FILE
ASSIGN TO DISK.
SELECT REGION-REPORT-FILE
ASSIGN TO 'COMPANY-BY-REGION.RPT'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD COMPANY-FILE.
01 COMPANY-RECORD.
02 COM-REGION PIC X.
02 FILLER PIC X.
02 COM-NAME PIC X(20).
SD COMPANY-SORT-FILE.
01 COMPANY-SORT-RECORD.
02 SORT-REGION PIC X.
02 FILLER PIC X.
02 SORT-NAME PIC X(20).
FD REGION-REPORT-FILE.
01 REGION-REPORT-RECORD PIC X(20).
WORKING-STORAGE SECTION.
01 SORTED-DATA-REMAINS PIC X VALUE 'Y'.
88 NO-SORTED-DATA-REMAINS VALUE 'N'.
01 WS-SORTED-RECORD.
02 WS-REGION PIC X.
02 FILLER PIC X.
02 WS-NAME PIC X(20).
01 REGION-REPORT-HEADER.
02 FILLER PIC X(18) VALUE 'COMPANY IN REGION '.
02 HEAD-REGION PIC X VALUE SPACE.
02 FILLER PIC X VALUE ':'.
01 REGION-DETAIL.
02 DET-NAME PIC X(20).
01 PRIOR-REGION PIC X.
PROCEDURE DIVISION.
WRITE-COMPANY-BY-REGION-REPORT.
SORT COMPANY-SORT-FILE
ASCENDING KEY SORT-REGION
SORT-NAME
USING COMPANY-FILE
OUTPUT PROCEDURE OUTPUT-CO-BY-REGION-REPORT
STOP RUN
.
OUTPUT-CO-BY-REGION-REPORT.
OPEN OUTPUT REGION-REPORT-FILE
PERFORM UNTIL NO-SORTED-DATA-REMAINS
RETURN COMPANY-SORT-FILE INTO WS-SORTED-RECORD
AT END SET NO-SORTED-DATA-REMAINS TO TRUE
NOT AT END
PERFORM WRITE-COMPANY-RECORD
END-PERFORM
CLOSE REGION-REPORT-FILE
.
WRITE-COMPANY-RECORD.
IF HEAD-REGION = SPACE
OR WS-REGION NOT = PRIOR-REGION
PERFORM PRINT-HEADER
END-IF
MOVE WS-NAME TO DET-NAME
WRITE REGION-REPORT-RECORD FROM REGION-DETAIL
.
PRINT-HEADER.
IF HEAD-REGION NOT = SPACE
MOVE SPACES TO REGION-REPORT-RECORD
WRITE REGION-REPORT-RECORD
END-IF
MOVE WS-REGION TO HEAD-REGION
PRIOR-REGION
WRITE REGION-REPORT-RECORD FROM REGION-REPORT-HEADER
.
You should do this in a single pass of the file. Instead of just checking one item at a time, check all of them in the single pass...
Related
I've been trying to read my input and write it to output file but can't find any mistakes in code. The JCL I'm submiting is good, because it was written by my mainframe lecturer, so I know the problem is somewhere in COBOL code... double checked everything, tried to find something with him in class - worthless... Line alignment, spacing, etc. are Ok I believe.
IDENTIFICATION DIVISION.
PROGRAM-ID. NAME.
AUTHOR. MYNAME.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INPUT10 ASSIGN TO INPUTFIL.
SELECT OUTPUT10 ASSIGN TO OUTFIL.
DATA DIVISION.
FILE SECTION.
FD INPUT10
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
RECORD CONTAINS 80 CHARACTERS.
01 PLAYER-DETAILS.
03 PLAYER-ID PIC 9(07).
03 PLAYER-NAME.
05 NAME PIC X(08).
05 INITIALS PIC A(02).
03 PLAYER-BDAY PIC 9(08).
03 PLAYER-NR PIC 9(02).
FD OUTPUT10
BLOCK CONTAINS 0 RECORDS
RECORDING MODE IS F
RECORD CONTAINS 80 CHARACTERS.
01 OUTPUT-DETAILS.
03 OUTPUT-ID PIC 9(07).
03 OUTPUT-NAME.
05 O-NAME PIC X(08).
05 O-INITIALS PIC A(02).
03 OUTPUT-BDAY PIC 9(08).
03 OUTPUT-NR PIC 9(02).
WORKING-STORAGE SECTION.
01 WS-INDICATORS.
10 WS-EOF-IND PIC X(01) VALUE 'N'.
88 WS-END-OF-FILE VALUE 'Y'.
PROCEDURE DIVISION.
MAINFLOW.
OPEN INPUT INPUT10
OPEN OUTPUT OUTPUT10
READ INPUT10
AT END SET WS-END-OF-FILE TO TRUE
END-READ
PERFORM UNTIL WS-END-OF-FILE
MOVE PLAYER-ID TO OUTPUT-ID
MOVE NAME TO O-NAME
MOVE INITIALS TO O-INITIALS
MOVE PLAYER-BDAY TO OUTPUT-BDAY
MOVE PLAYER-NR TO OUTPUT-NR
READ INPUT10
AT END SET WS-END-OF-FILE TO TRUE
END-READ
WRITE OUTPUT10
END-WRITE
END-PERFORM
CLOSE INPUT10
CLOSE OUTPUT10
STOP RUN.
here is the code of JCL
//useridX JOB ,
// MSGCLASS=H,
// MSGLEVEL=(1,1),
// CLASS=A,
// REGION=0M,
// NOTIFY=&SYSUID
//COBOL1 EXEC IGYWCLG,REGION=50M,
// PARM.COBOL='TEST,RENT,APOST,OBJECT,NODYNAM,LIB,SIZE(1048376)'
//COBOL.STEPLIB DD DSN=IGY420.SIGYCOMP,
// DISP=SHR
//COBOL.SYSIN DD DISP=SHR,DSN=userid.KURS.COBOL(PROG2)
//GO.INPUTFIL DD DISP=SHR,DSN=userid.KURS.PLAYERS
//GO.OUTFIL DD DISP=SHR,DSN=userid.KURS.REPORT
and it works for other students, so I'm pretty sure the cause of maxcc=12 is COBOL's part
Any suggestions?
Thanks.
P.S. I cannot check my job logs - something is wrong with my mainframe account or mainframe itself. This is the main cause why I can't find the problem
OK - I have run this successfully after some code changes. You really need to get your output sorted - are you using the correct MSGCLASS. Check - do not assume.
Program errors:
Look at PROGRAM-ID
Look at record lengths and compare to what you describe the record lengths of the files
Look at what you are writing.
Improvements:
Always check your status after any file operation - OPEN READ WRITE CLOSE.
Your indentation is not good. If it is on your machine then take more care when posting.
The input file and output file is declared as Fixed and is of length 80.
But both input and output variables are of length less than 80. They have length of 27.So in player-details and output details add filler variable with length 80 - 27 = 53
Also make sure the input and output file length are 80.
I have a COBOL program that requires a transaction number every run of the program. I am planning to get the last transaction number from the file and add 1 to it. The problem is I don't know how to get the last recorded value.
IDENTIFICATION DIVISION.
PROGRAM-ID. INVENTORY-SYS.
AUTHOR. LINSEY.
DATE-WRITTEN. 2/22/2015.
DATE-COMPILED. 2/22/2015.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT MASTER-FILE ASSIGN TO "inventory-file.txt"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD MASTER-FILE.
01 IN-RECORDS.
02 IN-CODE PIC 9(7).
02 IN-NAME PIC X(30).
02 IN-PRICE PIC 9(3).
02 IN-STOCK PIC 9(4).
WORKING-STORAGE SECTION.
01 WS-EOF PIC A(1).
PROCEDURE DIVISION.
100-READ-FILE.
OPEN I-O MASTER-FILE.
PERFORM UNTIL WS-EOF = "Y"
READ MASTER-FILE
AT END
MOVE 'Y' TO WS-EOF
NOT AT END
DISPLAY IN-RECORDS
END-READ
END-PERFORM
CLOSE MASTER-FILE.
STOP RUN.
This is the sample program. The problem is it retrieves all the records from the file I only the last record from "inventory-file.txt"
Well, you are insistent, so:
IDENTIFICATION DIVISION.
PROGRAM-ID. INVENTORY-SYS.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT MASTER-FILE ASSIGN TO "inventory-file.txt"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS w-if-in-file-status.
DATA DIVISION.
FILE SECTION.
FD MASTER-FILE.
01 IN-RECORDS.
02 IN-CODE PIC 9(7).
02 IN-NAME PIC X(30).
02 IN-PRICE PIC 9(3).
02 IN-STOCK PIC 9(4).
WORKING-STORAGE SECTION.
01 w-if-in-file-status PIC XX.
88 master-file-status-good VALUE ZERO "10".
88 end-of-master-file VALUE "10".
01 w-save-code PIC 9(7).
PROCEDURE DIVISION.
OPEN INPUT MASTER-FILE
[code to check FILE STATUS field]
PERFORM UNTIL end-of-master-file
READ MASTER-FILE
[code to check FILE STATUS field]
MOVE IN-CODE to w-save-code
END-PERFORM
DISPLAY ">" w-save-code "<"
CLOSE MASTER-FILE
[code to check FILE STATUS field]
GOBACK
.
I don't know why your IN-CODE field is numeric. Are you going to do calculations with it?
You keep a Control File. That has a date (to match to the Business Date file) a logical file-name, an environment and the last transaction number.
You maintain that file, checking everything as you do so.
I'm not sure of OpenCobol supports it, but you might try using OPEN/REVERSED. It is an old school way of reading a tape backwards, or these days, a virtual tape. I've no idea if it is implemented, but it is an easy way to read a sequential file backwards.
IDENTIFICATION DIVISION.
PROGRAM-ID. PROGRAM1.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT EMP-GRADE ASSIGN TO 'input.txt'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD EMP-GRADE.
01 NEWFILE.
05 FS-EMPID PIC 9(5).
05 FS-NAME PIC A(5).
05 FS-STREAM PIC X(5).
05 FS-GRADE PIC A(1).
05 FILLER PIC X(64).
WORKING-STORAGE SECTION.
01 WS-EOF PIC A(1) VALUE "N".
01 WS-STATUS PIC X(2).
PROCEDURE DIVISION.
MAIN-PARA.
OPEN INPUT EMP-GRADE.
PERFORM PARA1 THRU PARA1-EXIT UNTIL WS-EOF="Y".
CLOSE EMP-GRADE.
STOP RUN.
MAIN-PARA-EXIT.
EXIT.
PARA1.
READ EMP-GRADE
AT END MOVE "Y" TO WS-EOF
NOT AT END
IF FS-GRADE='A'
DISPLAY FS-EMPID , FS-NAME , FS-STREAM , FS-GRADE
END-IF
END-READ.
PARA1-EXIT.
EXIT.
input provided:
1234 sita comp A
2345 tina main B
5689 riya math A
but the output is coming :
1234 sita comp A
It is reading only the first record.
As Brian Tiffin is hinting at in the comments, it is your data which is the problem.
This:
05 FILLER PIC X(64).
Means that your records should be 64 bytes longer than they are.
If you have a fixed-length record, or only fixed-length records, under an FD, then the data all have to be the same length, and equal to what you have defined in your program.
It means, and behaviour depends on compiler, you only have one record as far as the COBOL program is concerned.
A good way to spot such things is to always count your input records, and count your output records, and records which should not be selected for output. You can then easily tell if anything has fallen between a crack.
Leaving that aside, here's your program with some adjustments:
IDENTIFICATION DIVISION.
PROGRAM-ID. PROGRAM1.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT EMP-GRADE ASSIGN TO 'input.txt'
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-STUDENT-GRADE-STATUS.
DATA DIVISION.
FILE SECTION.
FD EMP-GRADE.
01 NEWFILE.
05 FS-EMPID PIC 9(5).
05 FS-NAME PIC X(5).
05 FS-STREAM PIC X(5).
05 FS-GRADE PIC X(1).
05 FILLER PIC X(64).
WORKING-STORAGE SECTION.
01 WS-STUDENT-GRADE-STATUS PIC X(2).
88 END-OF-STUDENT-GRADE-FILE VALUE "10".
88 ERROR-ON-STUDENT-GRADE-FILE VALUE ZERO.
PROCEDURE DIVISION.
OPEN INPUT EMP-GRADE
* perform a paragraph to check FILE STATUS field is zero, using an 88.
PERFORM PRIMING-READ
PERFORM PROCESS-STUDENT-GRADE-FILE
UNTIL END-OF-STUDENT-GRADE-FILE
CLOSE EMP-GRADE
* perform a paragraph to check FILE STATUS field is zero, using an 88.
GOBACK
.
PRIMING-READ.
PERFORM READ-STUDENT-GRADE
.
READ-STUDENT-GRADE.
READ EMP-GRADE
* perform a paragraph to check FILE STATUS field is zero, using an 88.
.
PROCESS-STUDENT-GRADE-FILE.
IF FS-GRADE='A'
* To see the problem with your data, DISPLAY the 01-level
DISPLAY NEWFILE
DISPLAY FS-EMPID
FS-NAME
FS-STREAM FS-GRADE
END-IF
PERFORM READ-STUDENT-GRADE
.
If you use the FILE STATUS field, you should check it. Since you use it, you can use it to check for end-of-file without the AT END. If you use a "priming read" you don't need the AT END/NOT AT END tangle. If you code the minimum of full-stops/periods in the PROCEDURE DIVISION, you won't have a problem with them. Commas are never needed, so don't use them. Format your program for human readability. Use good descriptive names for everything. The THRU on a PERFORM invites the use of GO TO. As a learning, avoid the invitation.
If your class itself enforces particular ways to code COBOL, you'll have to use those ways. If so, I'd suggest you do both. The first couple of times at least, submit both to your tutor. Even if they tell you not to do that, continue doing dual examples when given tasks (just don't submit them any more). There is no reason for you to start off with bad habits.
Keep everything simple. If your code looks bad, make it look good through simplification and formatting.
Remember also that COBOL is all about fixed-length stuff. Get's us back to your original problem.
I'm a beginner to COBOL, and i'm wondering what would happen if i did something like the following:
(I know that the below code isnt runnable cobol, its just there for example)
foo pic x(5)
accept foo
and the user types in a string that is only 3 characters long (e.g. yes)
would the value of foo be just "yes"? or would it fill the all 5 characters as specified at creation (for example: "(space)(space)yes" or "yes(space)(space)", or is it something else?
Thanks!
here is my code
000100 IDENTIFICATION DIVISION.
000200 *--------------------
000300 PROGRAM-ID. ZIPCODES.
000400 *--------------------
000500 ENVIRONMENT DIVISION.
000600 *--------------------
000700 CONFIGURATION SECTION.
000800 INPUT-OUTPUT SECTION.
000900 FILE-CONTROL.
001000 SELECT PRT ASSIGN TO UT-S-PRTAREA.
001100
001200 DATA DIVISION.
001300 *-------------
001400 FILE SECTION.
001500 FD PRT
001600 RECORD CONTAINS 80 CHARACTERS
001700 DATA RECORD IS LINE-PRT.
001800 01 LINE-PRT PIC X(80).
001900
002000 WORKING-STORAGE SECTION.
002100 *-----------------------
002200 EXEC SQL INCLUDE SQLCA END-EXEC.
002300
002310 01 done.
002320 02 donevar PIC x(5) VALUE 'done '.
002400 01 ZIP-RECORD.
002500 02 ZIP PIC X(5).
002600 02 ZCITY PIC X(20).
002700 02 ZSTATE PIC X(2).
002800 02 ZLOCATION PIC X(35).
002900
003000 01 H1.
003100 02 COLUMN-1 PIC X(8) VALUE 'Zip-Code'.
003200 02 FILLER PIC X(2).
003300 02 COLUMN-2 PIC X(5) VALUE 'State'.
003400 02 FILLER PIC X(2).
003500 02 COLUMN-3 PIC X(4) VALUE 'City'.
003600 02 FILLER PIC X(16).
003700 02 COLUMN-4 PIC X(14) VALUE 'Location Text'.
003800 02 FILLER PIC X(29).
003900
004000 01 L1.
004100 02 ZIP-L1 PIC X(5).
004200 02 FILLER PIC X(5).
004300 02 STATE-L1 PIC X(2).
004400 02 FILLER PIC X(5).
004500 02 CITY-L1 PIC X(20).
004600 02 LOCTXT-L1 PIC X(35).
004700 02 FILLER PIC X(28).
004800
004900 PROCEDURE DIVISION.
005000 *------------------
005100 BEGIN.
005200 OPEN OUTPUT PRT.
005220 PERFORM ZIP-LOOKUP UNTIL ZIP = done.
005600 PROG-END.
005700 CLOSE PRT.
005800 GOBACK.
005900 *****************************************************
006000 * zip code lookup *
006100 *****************************************************
006200 ZIP-LOOKUP.
006300 DISPLAY 'enter 5 digit zip code'
006400 ACCEPT ZIP
006500 EXEC SQL
006600 SELECT * INTO :ZIP-RECORD FROM ZBANK.ZIPCODE
006700 WHERE ZIP = :ZIP
006800 END-EXEC.
006801 PERFORM PRINT-H1.
006802 PERFORM PRINT-L1.
006900 PRINT-H1.
007000 MOVE H1 TO LINE-PRT
007100 WRITE LINE-PRT.
007200 PRINT-L1.
007300 MOVE ZIP TO ZIP-L1
007400 MOVE ZSTATE TO STATE-L1
007500 MOVE ZCITY TO CITY-L1
007510 STRING ZSTATE DELIMITED BY " ",", ",
007520 ZCITY DELIMITED BY SIZE INTO LOCTXT-L1
007700 MOVE L1 TO LINE-PRT
007800 WRITE LINE-PRT.
I'm trying to write the zstate before the zcity, and having it keep asking for ZIP codes as long as the input isnt 'done'
The first 5 characters entered will be moved to FOO. If fewer than 5 characters are entered then they will be placed in the left hand positions of FOO and the remaining characters (to the right) will be filled with spaces. If the user enters more than 5 charcters then only the first 5 are moved.
So to use your example if the user typed "yes" then FOO would contain "yesbb"
Best thing to do is try it!
Edit in response to updated question...
I think your problem is that the condition needed to terminate the loop is set in the beginning of the loop body and
not at the end. Here are a couple of commonly used techniques to solve this problem:
Pre loop read
DISPLAY 'Enter a 5 digit zip code'
ACCEPT ZIP
PERFORM ZIP-LOOKUP UNTIL ZIP = done.
...
...
ZIP-LOOKUP.
EXEC SQL
SELECT * INTO :ZIP-RECORD FROM ZBANK.ZIPCODE
WHERE ZIP = :ZIP
END-EXEC.
PERFORM PRINT-H1.
PERFORM PRINT-L1.
* Now get next zip code or 'done'
DISPLAY 'Enter a 5 digit zip code'
ACCEPT ZIP
.
Guard against setting terminating condition within the loop
PERFORM ZIP-LOOKUP UNTIL ZIP = done.
...
...
ZIP-LOOKUP.
DISPLAY 'Enter a 5 digit zip code'
ACCEPT ZIP
IF ZIP NOT = DONE
EXEC SQL
SELECT * INTO :ZIP-RECORD FROM ZBANK.ZIPCODE
WHERE ZIP = :ZIP
END-EXEC
PERFORM PRINT-H1
PERFORM PRINT-L1
END-IF
.
Either one of the above should solve your problem. However, I would suggest trying to update your coding style to include
COBOL-85 constructs. The first example above might be coded as follows:
DISPLAY 'Enter a 5 digit zip code'
ACCEPT ZIP
PERFORM UNTIL ZIP = done
EXEC SQL
SELECT * INTO :ZIP-RECORD FROM ZBANK.ZIPCODE
WHERE ZIP = :ZIP
END-EXEC
PERFORM PRINT-H1
PERFORM PRINT-L1
DISPLAY 'Enter a 5 digit zip code'
ACCEPT ZIP
END-PERFORM
.
The ZIP-LOOKUP paragraph has been in-lined into the PERFORM statement. For short sections of code I find this style much more
readable.
Also notice single sentence paragraphs (only one period at the end of a paragraph). When COBOL-85 scope terminators are used (eg. END-xxx)
the need for mulitple sentences per paragraph goes away - and in fact - they should be avoided.
Another COBOL construct that you could make use of here is the 88 LEVEL. You could use it as follows:
01 ZIP-RECORD.
02 ZIP PIC X(5).
88 DONE VALUE 'done '.
...
...
You no longer need donevar at all. Replace your original test:
IF ZIP = DONE
with:
IF DONE
The above will be true whenever the variable ZIP contains the value "donebb". One advantage of
doing this (other than saving one variable declaration) is that a single 88 LEVEL name can be assigned
several values, as in:
01 ZIP-RECORD.
02 ZIP PIC X(5).
88 DONE VALUE 'done ',
'quit ',
'stop '.
When the user enters any one of done, quit or stop the 88 level name DONE evaluates to true.
Finally, I presume this is just a skeleton of the program and that the finished version will be checking for I/O errors, bad SQL codes
and do basic ZIP code validation. If not, you can expect a lot of trouble down the road.
COBOL Reference material
Unfortunately there are very few good up to date resources for learning COBOL. However, one of the
books I would recommend is Advanced Cobol 3rd Edition by DeWard Brown.
This book provides many examples and explanations regarding COBOL program development. It also identifies whether a
construct is rarely used, obsolete or essential. This is good to know since you should be developing new code using modern COBOL
programming techniques (I continue to see a lot of new COBOL developed using pre-COBOL 85 coding practice - and it is horrible).
An open source
guide is the OpenCOBOL Programmers Guide. This targets OpenCOBOL but
much of it is applicable to any flavour of COBOL.
Finally, there are several vendors guides and manuals, many of which are available on the internet. For
example Enterprise COBOL for z/OS Language Reference and
Enterprise COBOL for z/OS Programming guide are
freely available. Microfocus COBOL
guides are also available. Search any you will find...
OK so I'm doing assignment but then I found that I was asked to add page numbers and change pages for each 4 records. Since it's an online course and I don't think there is anything about page numbers in lecture videos. So the main problems are
To add a heading that contains date and page number,
Print 4 records per page, which means page needs to be changed after printing 4 records.
I really have no idea how to do this.
Here is the code I have finished:
ENVIRONMENT DIVISION.
FILE-CONTROL. SELECT STOCK-IN ASSIGN TO 'F:/CS201S13/PROJECT2.TXT'
ORGANIZATION IS LINE SEQUENTIAL.
SELECT STOCK-OUT ASSIGN TO 'F:/CS201S13/PROJECT2OUTPUT.TXT'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD STOCK-IN.
01 STOCK-RECORD.
05 ST-TRANSACTION-INFORMATION.
10 ST-TRANSACTION-SHARES PIC 9(3).
10 ST-TRANSACTION-STOCK PIC X(14).
05 ST-PURCHASE-INFORMATION.
10 ST-PURCHASE-PRICE PIC 9(5)V99.
10 ST-PURCHASE-DATE.
15 ST-PURCHASE-YEAR PIC 99.
15 ST-PURCHASE-MONTH PIC 99.
15 ST-PURCHASE-DAY PIC 99.
05 ST-SALE-INFORMATION.
10 ST-SALE-PRICE PIC 9(5)V99.
10 ST-SALE-DATE.
15 ST-SALE-YEAR PIC 99.
15 ST-SALE-MONTH PIC 99.
15 ST-SALE-DAY PIC 99.
FD STOCK-OUT.
01 STOCK-RECORD-OUT.
05 ST-TRANSACTION-INFORMATION-OUT.
10 ST-TRANSACTION-SHARES-OUT PIC 9(3).
10 ST-TRANSACTION-STOCK-OUT PIC X(14).
05 TOTAL-PURCHASE PIC 9(8)V99.
05 PIC X(4).
05 TOTAL-SALE PIC 9(8)V99.
05 PIC X(4).
05 TOTAL-PROFIT PIC 9(8)V99.
05 PIC X(4).
05 ST-PURCHASE-DATE-OUT.
10 ST-PURCHASE-YEAR-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-PURCHASE-MONTH-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-PURCHASE-DAY-OUT PIC 99.
05 PIC X(4).
05 ST-SALE-DATE-OUT.
10 ST-SALE-YEAR-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-SALE-MONTH-OUT PIC 99.
10 PIC X VALUE '/'.
10 ST-SALE-DAY-OUT PIC 99.
05 PIC X(4).
05 RECORD-OUT PIC 9 VALUE 0.
05 PAGE-OUT PIC 9.
WORKING-STORAGE SECTION.
01 ARE-THERE-MORE-RECORDS PIC XXX VALUE 'YES'.
01 IS-THIS-PAGE-FULL PIC XXX VALUE 'NO '.
PROCEDURE DIVISION.
100-MAIN-PROCESS.
OPEN INPUT STOCK-IN
OUTPUT STOCK-OUT
MOVE ST-TRANSACTION-INFORMATION TO ST-TRANSACTION-INFORMATION-OUT
PERFORM UNTIL ARE-THERE-MORE-RECORDS = 'NO '
READ STOCK-IN
AT END
MOVE 'NO ' TO ARE-THERE-MORE-RECORDS
NOT AT END
PERFORM 200-PROCEDURE-RTN
ADD 1 TO RECORD-OUT
END-READ
END-PERFORM
CLOSE STOCK-IN
STOCK-OUT
STOP RUN.
200-PROCEDURE-RTN.
IF RECORD-OUT = 4
MOVE 'YES' TO IS-THIS-PAGE-FULL
MOVE 0 TO RECORD-OUT
MOVE 'NO ' TO IS-THIS-PAGE-FULL
ADD 1 TO PAGE-OUT
END-IF
MULTIPLY ST-PURCHASE-PRICE BY ST-TRANSACTION-SHARES GIVING TOTAL-PURCHASE
MULTIPLY ST-SALE-PRICE BY ST-TRANSACTION-SHARES GIVING TOTAL-SALE
SUBTRACT TOTAL-PURCHASE FROM TOTAL-SALE GIVING TOTAL-PROFIT
WRITE STOCK-RECORD-OUT.
You are both close, and far away.
"Close" because you need a little bit of code in between setting IS-THIS-PAGE-FULL to YES and NO.
"Far away" as you have quite a lot to do rather than just "patch up" what you have.
Is the program writing an output file (STOCK-OUT) and a report, or is STOCK-OUT the report? If it is a report, change the names so that it is clear that it is a report, not an output file.
Don't worry if this seems a lot. You should be learning how to Program in Cobol, as well as learning Cobol. Doesn't happen overnight.
In no particular order:
Include FILE-STATUS checking for all IO operations on all files, always. At the moment, if your input fails to open and the system does not fail the program (even if yours does, you are presumably learning Cobol to be able to work with any system, not just the one you have) then no records will be read, your "end of file test" will never be YES and you'll have a BFL (Big Fat Loop). With the FILE-STATUS checking, produce useful messages, including key/reference/record number as appropriate for failed READ or WRITE.
You may feel that this is a lot of work. However, put together some "template" files with all the stuff in, and then paste (or even COPY) those into your program each time.
You have VALUE clause in the FD. These will not do what you think.
You have single digit for your page count, which is unlikely to have general application.
Why use YES and NO as literals? Look at the SET verb, in relation to "condition names", use 88's for tests and "flags/switches".
You have "MOVE ST-TRANSACTION-INFORMATION" after the input is opened but before a record is read, and only have one reference to it in the program. This is not going to work.
For reading files, have a look at the "priming read" approach.
read input
loop until end-of-file (88 on file-status)
process data
read input
end-loop
This avoids the AT END/NOT AT END, allows processing of headers (if present) and "empty files" without clogging-up the main logic. The code "expands" with headers/trailers (including the correct number of them), sequence-checking of keys, etc, but you only need to code it once then "template" it.
According to your VALUE clauses in your FD, you expect RECORD-OUT to be zero, so the test for 4 will actually get you five on the first page, and four thereafter.
You always assume there will be a "profit" (a positive amount), which is not realistic, yet you don't allow a signed value for the "profit".
Now, for the report.
For your report FD, just make it a simple thing, length of your print line.
In WORKING-STORAGE, define data for the headings and titles that you need. Define data for a print line. Since you're in the WORKING-STORAGE, put VALUEs for everything which will not have data MOVEd to it in the PROCEDURE DIVISION.
When you have written four items (or when your program tells you this) and you have a fifth, write the headings and titles, remembering to update the page number.
I say "or when your program tells you this" because you can set your original value of "records written" to 4. Comment it, so that it is clear that it is what you want, and why you want it. The reason is, you don't have to then deal with "first time" headings and othe things. For first time, or on a "contol break" (I guess you'll get to those soon) set the " done on a page already" to the maximum for a page, and the headings will pop out when you want.
Format the print line. PERFORM a para to print it (which is where the "page full" test will be).
Note: You can use VALUEs for your "/"s in the dates, or you can use the "/" editing character in the PICture, like this:
05 an-input-date PIC X(8) (can be other definitions).
...
05 date-to-print PIC X(4)/XX/XX.
...
MOVE an-input-date TO date-to-print
I like to see that you are using "minimal full-stops/periods". You can go a little further.
MOVE an-input-date TO date-to-print
.
Then you get your final full-stop/period in a paragraph, without having it "attached" to any particular line of code, which makes "tossing code around" easier, as you don't have to think "do I need/not need that full-stop/period there".
You could also look through some of the Cobol questions here, and get a handle on some general tips and advice.
This may or may not help, if LINAGE is not supported you'll have to do some explicit counting.
*****************************************************************
* Example of LINAGE File Descriptor
* Author: Brian Tiffin
* Date: 10-July-2008
* Tectonics: $ cobc -x linage.cob
* $ ./linage <filename ["linage.cob"]>
* $ cat -n mini-report
*****************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. linage-demo.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
select optional data-file assign to file-name
organization is line sequential
file status is data-file-status.
select mini-report assign to "mini-report".
DATA DIVISION.
FILE SECTION.
FD data-file.
01 data-record.
88 endofdata value high-values.
02 data-line pic x(80).
FD mini-report
linage is 16 lines
with footing at 15
lines at top 2
lines at bottom 2.
01 report-line pic x(80).
WORKING-STORAGE SECTION.
01 command-arguments pic x(1024).
01 file-name pic x(160).
01 data-file-status pic 99.
01 lc pic 99.
01 report-line-blank.
02 filler pic x(18) value all "*".
02 filler pic x(05) value spaces.
02 filler pic x(34)
VALUE "THIS PAGE INTENTIONALLY LEFT BLANK".
02 filler pic x(05) value spaces.
02 filler pic x(18) value all "*".
01 report-line-data.
02 body-tag pic 9(6).
02 line-3 pic x(74).
01 report-line-header.
02 filler pic x(6) VALUE "PAGE: ".
02 page-no pic 9999.
02 filler pic x(24).
02 filler pic x(5) VALUE " LC: ".
02 header-tag pic 9(6).
02 filler pic x(23).
02 filler pic x(6) VALUE "DATE: ".
02 page-date pic x(6).
01 page-count pic 9999.
PROCEDURE DIVISION.
accept command-arguments from command-line end-accept.
string
command-arguments delimited by space
into file-name
end-string.
if file-name equal spaces
move "linage.cob" to file-name
end-if.
open input data-file.
read data-file
at end
display
"File: " function trim(file-name)
" open error or empty"
end-display
go to early-exit
end-read.
open output mini-report.
write report-line
from report-line-blank
end-write.
move 1 to page-count.
accept page-date from date end-accept.
move page-count to page-no.
write report-line
from report-line-header
after advancing page
end-write.
perform readwrite-loop until endofdata.
display
"Normal termination, file name: "
function trim(file-name)
" ending status: "
data-file-status
end-display.
close mini-report.
* Goto considered harmful? Bah! :)
early-exit.
close data-file.
exit program.
stop run.
****************************************************************
readwrite-loop.
move data-record to report-line-data
move linage-counter to body-tag
write report-line from report-line-data
end-of-page
add 1 to page-count end-add
move page-count to page-no
move linage-counter to header-tag
write report-line from report-line-header
after advancing page
end-write
end-write
read data-file
at end set endofdata to true
end-read
.
*****************************************************************
* Commentary
* LINAGE is set at a 20 line logical page
* 16 body lines
* 2 top lines
* A footer line at 15 (inside the body count)
* 2 bottom lines
* Build with:
* $ cobc -x -Wall -Wtruncate linage.cob
* Evaluate with:
* $ ./linage
* This will read in linage.cob and produce a useless mini-report
* $ cat -n mini-report
*****************************************************************
END PROGRAM linage-demo.