I have a copy book like :
01 MY-STRUCTURE.
05 STRUCTURE-NAME PIC X(20).
05 STRUCTURE-DATE PIC X(8).
05 STRUCTURE-LIB PIC 9(3).
How can I get the name of my fields?
I want to have a program that can get any copybook and write dynamically its fields and values.
I want to write it to an output file like this :
"STRUCTURE-NAME-> TOTO"
"STRUCTURE-DATE-> 19780301"
"STRUCTURE-LIB -> 123"
In the z/OS world, depending upon your compiler level, you can use the debugging information generated by the compiler to do introspection on your names. To do this, you will need to save your ADATA at compile time.
I have a two possible solutions:
You save the names of the fields of the copy in a internal table. And secuential reads the table for your write your output.
Dynamical allocation, is more complicated but it's possible. You allocate the copy in your program using bpxwdyn (subroutine IBM).
You create a subprogram, with name of copy, the length is fixed (80), the name of library of copys, and allocate this copy in subprogram. Then, You can fill a internal table with his datas.
Eg:
http://mainframe-tips-and-tricks.blogspot.com.es/2011/12/cobol-sample-program-for-dynamic-file.html
Related
Hi i am learning cobol from tutorialpoints and every program from there works as i've tested them in OpenCobolIDE(some needed a little editing). Then i came across the File Handling chapter and in there the program had a lot of errors. I did manage to rewrite the program until it didn't show me any errors but it doesn't do anything.
Here's my code:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT STUDENT ASSIGN TO
'C:\Cobol\FIle Handling\input.txt'
ORGANIZATION IS INDEXED
ACCESS IS RANDOM
RECORD KEY IS STUDENT-ID
FILE STATUS IS FS.
DATA DIVISION.
FILE SECTION.
FD STUDENT.
01 STUDENT-FILE.
05 STUDENT-ID PIC 9(5).
05 NAME PIC A(25).
WORKING-STORAGE SECTION.
01 WS-STUDENT-FILE.
05 WS-STUDENT-ID PIC 9(5).
05 WS-NAME PIC A(25).
01 FS PIC 9(02).
PROCEDURE DIVISION.
OPEN I-O STUDENT.
MOVE 20005 TO STUDENT-ID.
READ STUDENT RECORD INTO WS-STUDENT-FILE
KEY IS STUDENT-ID
INVALID KEY DISPLAY 'Invalid Key'
NOT INVALID KEY DISPLAY WS-STUDENT-FILE
END-READ.
CLOSE STUDENT.
STOP RUN.
This is the text file:
20003 Mohtashim M.
20004 Nishant Malik
20005 Amitabh Bachhan
The result should be the text:
20005 Amitabh Bachhan
It's doing something: It's reading the file. But that's all; you didn't ask for it to display or do anything else beyond reading the record into memory. You might want to look at using the DISPLAY statement or maybe create another file to write the output to.
Might I make a couple of suggestions?
In modern COBOL, stylistically, you don't put a period after everything in the procedure division -- you only put it in where it is necessary. For example:
PROCEDURE DIVISION.
OPEN I-O STUDENT
MOVE 20005 TO STUDENT-ID
READ STUDENT RECORD INTO WS-STUDENT-FILE
KEY IS STUDENT-ID
INVALID KEY DISPLAY 'Invalid Key'
NOT INVALID KEY DISPLAY WS-STUDENT-FILE
END-READ
CLOSE STUDENT
STOP RUN
.
Although the compiler doesn't care about spaces and returns, if I were you, I'd try to indent my code a bit better (I like how I indented the above :-) ). It's up to you and a lot of people like to do it differently, but if you are consistent you can spot problems that might sneak through your code.
Edit: I didn't notice that you were reading with a key from a text file. So, either you need to:
read from a pre-built indexed file, or
read the file sequentially and search for what you want by comparing what you read for the student id you wanted.
I have fixed-point numbers in file, one in each line, in this format S9(6)V9(2) but when they are actually read, I'm getting non numeric errors while trying to put them in math operations. What is more, when I try to display them in program, a number that is written in file as 567123.45 is saved in variable as +567123.04. And for example the number from file 123.45 is saved in variable as +123.45.00 and it provokes the following error 'WS-VALUE' not numeric: '123.45 0' during a math operation. Why is that?
I'm using OpenCobolIDE 4.7.4 for Windows.
EDIT:
File has records of the following form separated by new lines (read by READ operation record after record):
01 WS-OPERATION.
05 WS-ID PIC A(2).
05 WS-CLIENT PIC 9(5).
05 WS-COUNTRY PIC A(4).
05 WS-VALUE PIC S9(6)V9(2).
The reason is that you try to un-edit a field. 567123.45 in the data is not conforming to PIC S9(6)V9(2) but to -9(6).9(2). - internal stored data vs. print-data.
Simply changing the definition and use MOVE WS-VALUE TO WS-VALUE-INTERNAL (which is defined like you want to) may work with a specific compiler (and specific data) but I'd go a different route:
I'd suggest to always validate the data before doing something with it (the file may be broken or external edited).
At least check the simple numeric data like WS-CLIENT for IS NUMERIC and either do a full validation on the data field WS-VALUE or at least use MOVE FUNCTION NUMVAL(WS-VALUE) TO WS-VALUE-INTERNAL.
I have written a code to match the date given by user(instream data) with the date mentioned in an input file. When the date matches, the record is moved to an output file. The program ran successfully. Now i am inserting a header in output file by declaring a variable 'header' in working-storage section, moving the 'header' field to output record and writing the output record. Though it is running successfully i have a doubt. I looked for it everywhere but unable to find it. The problem is-
The concept of buffer tells us that while declaring File Description(FD) it creates a structure. The file description(FD) given with all the records and fields creates a buffer structure. Now all the read/write operation happens through it.
If a program processes more than one file, a record buffer must be defined for each file.
To process all the records in an INPUT file, we must ensure that each record instance is copied from the file, into the record buffer, when required.
To create an OUTPUT file containing data records, we must ensure that each record is placed in the record buffer and then transferred to the file.
To transfer a record from an input file to an output file we must read the record into the input record buffer, transfer it to the output record buffer and then write the data to the output file from the output record buffer.
So when i am giving the date separately and moving it to the output record it is SURELY not going into that buffer which i created using FD in output record. So how is it going there?? If it is creating its own buffer then all data can be moved directly, what is the need of declaring FD??
This is the link to see the concept of buffer
[1]: http://www.csis.ul.ie/cobol/course/SequentialFiles1.htm
This is my code as given below-JUST EMPHASIZE ON STATEMENTS IN SLASHES
IDENTIFICATION DIVISION.
PROGRAM-ID. TXNRPT02 .
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT D01-TXN-FILE ASSIGN TO DETLFILE
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-INSERT-FILE-STATUS1.
SELECT D02-TXN-FILE ASSIGN TO DATEOUT
ORGANIZATION IS SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS WS-INSERT-FILE-STATUS2.
DATA DIVISION.
FILE SECTION.
FD D01-TXN-FILE. // CREATES A BUFFER
01 D01-TXN-REC.
05 TXN-DATE PIC 9(8).
05 FILLER PIC X(1).
05 TXN-NUMBER PIC 9(5).
05 FILLER PIC X(1).
05 SBACCT-CODE PIC 9(5).
05 FILLER PIC X(1).
05 TXN-AMOUNT PIC 9(5)V9(2).
05 FILLER PIC X(52).
FD D02-TXN-FILE. //CREATES A BUFFER STRUCTURE IN OUTPUT FILE
01 D02-TXN-REC.
05 TXN-DATE1 PIC 9(8).
05 FILLER PIC X(1).
05 TXN-NUMBER1 PIC 9(5).
05 FILLER PIC X(1).
05 SBACCT-CODE1 PIC 9(5).
05 FILLER PIC X(1).
05 TXN-AMOUNT1 PIC 9(5)V9(2).
05 FILLER PIC X(52).
WORKING-STORAGE SECTION.
01 HEAD1 // declaring header
02 FILLER PIC X(20).
02 TEXT PIC X(40) VALUE 'REPORT OF BANK'
01 FILE-STATUS PIC X(02) VALUE '00'.
88 EOF VALUE '10'.
88 SUCCESS VALUE '00'.
01 WS-INSERT-FILE-STATUS1 PIC X(2).
01 WS-INSERT-FILE-STATUS2 PIC X(2).
01 WS-DATE PIC 9(8). // DATE TO BE ACCEPTED FROM INSTREAM DATA
PROCEDURE DIVISION.
MAIN-PARA.
ACCEPT WS-DATE.
PERFORM OPEN-PARA.
PERFORM INIT-PARA.
PERFORM PROCESS-PARA UNTIL EOF.
PERFORM CLOSE-PARA.
STOP RUN.
OPEN-PARA. // OPENS INPUT AND OUTPUT FILES
OPEN INPUT D01-TXN-FILE.
DISPLAY 'INFILE OPEN STATUS' , WS-INSERT-FILE-STATUS1.
OPEN OUTPUT D02-TXN-FILE.
DISPLAY 'OUTFILE OPEN STATUS' ,WS-INSERT-FILE-STATUS2.
INIT-PARA. // INITIALIZES BOTH RECORDS
INITIALIZE D01-TXN-REC.
INITIALIZE D02-TXN-REC.
PROCESS-PARA.
MOVE HEAD1 TO D02-TXN-REC. // MOVING HEADER TO OUTPUT RECORD
WRITE D02-TXN-REC. // WRITING THE HEADER
READ D01-TXN-FILE
AT END MOVE '10' TO FILE-STATUS
NOT AT END PERFORM OPPO-PARA
END-READ.
OPPO-PARA. //PERFORMS EQUAL OPERATION ON BOTH DATES
IF WS-DATE = TXN-DATE
MOVE D01-TXN-REC TO D02-TXN-REC
WRITE D02-TXN-REC
END-IF.
CLOSE-PARA. // CLOSING BOTH FILES
CLOSE D01-TXN-FILE, D02-TXN-FILE.
DISPLAY 'INFILE CLOSE STATUS' , WS-INSERT-FILE-STATUS1.
DISPLAY 'OUTFILE CLOSE STATUS' , WS-INSERT-FILE-STATUS2.
I think you are over-complicating things.
In the FILE SECTION you logically connect a record-layout to a file that will be read, or to a record which will be written (to a file).
No storage is allocated in your COBOL program by the definition of the FD or any 01-levels subordinate to an FD. You simply establish a "map" which will be used to understand the data read from a file, or to use to construct the data which you will write to a file.
When you OPEN a file, some run-time code does that work behind the scenes, and will make the address of the first byte of your record layout(s) under the FD equal to the first byte of the piece of storage that will be used as the "buffer".
A "buffer" in this context is just a piece of storage which is the same size as your BLKSIZE and which will be used, without you having to be aware of it, to store records before they are actually written to the physical output file.
What happens when you WRITE a record is that the record-pointer simply gets changed to the first available byte after the current record.
It is similar when you read.
Although installation-dependent, mostly you will have the default number of buffers for a simple sequential file, which is five. Once a buffer is full the, for an output file, the IO-subsystem will be told it has data available for writing, and the filling will continue, asynchronously, with the next buffer area. What this does is reduce the amount of time your COBOL program is waiting for IO to complete.
It is similar, which obvious reversals, for the READ of a sequential file.
As a beginner, you don't need to know what the buffering is doing. Just keep it conceptually simple. You OPEN, you READ, the record is "under" the FD. You continue doing that until end-of-file, then you CLOSE. At the same time (usually) you OPEN, you WRITE, and you CLOSE at the end. What gets written is the data that is under the FD for the output file.
Also review READ ... INTO ... and WRITE ... FROM .... The concept is the same, it is just that COBOL will "move" the data to the FD for you, rather than you having detailed record-layouts under the FD. Whether you use READ file or READ file INTO record-layout is usually down to local standards for your site - do it the way your team does it.
You have now added some code to your Question.
First thing, you are writing your header inside a loop. You will get one header per record, which is probably not what you want.
Secondly, you've changed to use the FILE STATUS but not fully grasped how it works yet. You don't need the AT END/NOT AT END. The COBOL IO will maintain the FILE STATUS field. When end-of-file is identified by the COBOL run-time the FILE STATUS field will be set to "10", "automatically" as far as you are concerned.
Thirdly, review your use of INITIALIZE. There is never any point in INITIALIZE on an input record. READ always advances the record-pointer. The storage INITIALIZEd is the storage before the record you read. Even if it was not, what is the point of INITIALIZE, just to read a record over the top of the initialised storage? Same with your output.
All that your use of INITIALIZE is doing is using CPU time. Yes, it is not at all uncommon that this happens, but that is no excuse for you to pick up bad habits as well.
I have written the following COBOL program:
*************************************************************
* VERKOOP
*************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. VERKOOP.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT PRODUCTEN ASSIGN TO "BESTANDEN/PRODUCTEN"
ACCESS MODE IS RANDOM
ORGANIZATION IS INDEXED
RECORD KEY IS PRODUCTID
FILE STATUS IS WS-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD PRODUCTEN BLOCK CONTAINS 10 RECORDS.
01 PRODUCT.
02 PRODUCTID PIC X(6).
02 LEVERANCIERID PIC X(6).
02 AANTAL PIC 9(6).
WORKING-STORAGE SECTION.
77 FOUT PIC X.
88 PRODUCT-NIET-GEVONDEN VALUE 1.
77 WS-PRODUCTID PIC X(6).
77 WS-AANTAL PIC 9(6).
77 WS-FILE-STATUS PIC XX.
LINKAGE SECTION.
01 LS-PRODUCTID PIC X(6).
01 LS-AANTAL PIC 9(6).
PROCEDURE DIVISION.
* USING LS-PRODUCTID, LS-AANTAL.
MAIN.
PERFORM INITIALISEER
PERFORM LEES-PRODUCT-IN
PERFORM LEES-BESTAND
PERFORM SLUIT-BESTAND
STOP RUN.
INITIALISEER.
MOVE ZEROS TO PRODUCT
OPEN I-O PRODUCTEN.
* DISPLAY WS-FILE-STATUS..
LEES-PRODUCT-IN.
* MOVE LS-PRODUCTID TO WS-PRODUCTID
* MOVE LS-AANTAL TO WS-AANTAL.
DISPLAY "GEEF PRODUCTID OP: "
ACCEPT WS-PRODUCTID
DISPLAY "GEEF AANTAL OP: "
ACCEPT WS-AANTAL.
LEES-BESTAND.
* DISPLAY "LEES-BESTAND"
MOVE WS-PRODUCTID TO PRODUCTID
* DISPLAY PRODUCTID
READ PRODUCTEN INVALID KEY SET PRODUCT-NIET-GEVONDEN TO TRUE
END-READ
DISPLAY "END-READ" WS-FILE-STATUS
IF PRODUCT-NIET-GEVONDEN PERFORM FOUTJE
ELSE
MOVE WS-PRODUCTID TO PRODUCTID
SUBTRACT WS-AANTAL FROM AANTAL
PERFORM UPDATE-PRODUCT
END-IF.
UPDATE-PRODUCT.
REWRITE PRODUCT INVALID KEY PERFORM FOUTJE.
SLUIT-BESTAND.
* DISPLAY "SLUIT-BESTAND"
CLOSE PRODUCTEN.
FOUTJE.
DISPLAY "ER IS EEN FOUT OPGETREDEN"
* DISPLAY WS-FILE-STATUS
STOP RUN.
The idea is that I find a product by its productid in the file PRODUCTEN.dat and subtract the amount (aantal) by a given number. However everytime I run it I get the following error: WARNING - Implicit CLOSE of PRODUCTEN <"BESTANDEN/PRODUCTEN">. I don't really see the problem, the WS-FILE-STATUS line even gives me back a 00 status. I am 100% sure the product is in the file so I'm not trying to subtract from a non-existing product or anything.
UPDATE: I fixed it by assign PRODUCTEN to a newly declared file as the last one (somehow) got corrupt and was behaving in an unintended way.
To get that Implicit Close message, you must have a STOP RUN before you close the file.
You have a STOP RUN in paragraph FOUTJE, before the file is closed, so paragraph FOUTJE is being used.
You use paragraph FOUTJE in a PERFORM when PRODUCT-NIET-GEVONDEN is true.
PRODUCT-NIET-GEVONDEN is set to true on the INVALID KEY of the READ.
So INVALID KEY is true.
You get a FILE STATUS of ZERO. Unexpected, but fits what you have presented.
I don't have COBOL-IT and I don't know what OS you are using.
I also don't know in your set-up what a READ of a keyed file which does not explicitly reference a key does.
I don't know in any set-up, because I don't do it. If I'm doing a keyed read, I always specify the key.
I don't put data in the key on the file. I use a WORKING-STORAGE field for the key.
Why, well, implementation-dependent for the compiler, but unless your file is OPEN and unless there is a current record on the file, then the content, even the address, of a file record is/can be (implementation dependent) undefined.
As far as I am concerned, the KEY on the SELECT is to define the presence of the key on the file. The key you are using to READ the file obviously comes from elsewhere.
So, I would remove these:
MOVE ZEROS TO PRODUCT
MOVE WS-PRODUCTID TO PRODUCTID
I'd change this to include the KEY of WS-PRODUCTID
READ PRODUCTEN INVALID KEY SET PRODUCT-NIET-GEVONDEN TO TRUE
I'd not use INVALID KEY, I'd just use the value of WS-FILE-STATUS, which I'd expect to be "23" for "not found". I'd do the test with an 88. You then don't need your "flag" (FOUT and PRODUCT-NIET-GEVONDEN) anyway. Check the FILE STATUS field after each IO. This time you spelled your filename correctly, another time you won't and you may waste more time chasing your tail.
Work on consistent indentation, it will make your program easier to read, for you, and anyone else.
If you want to use DISPLAY to verify the logical path, you need to DISPLAY the value which is used to determine the logical path (FOUT in this case).
There are two "formats" of the READ statement. One is for sequential reads, one is for reads using a key. When each is reduced to its mandatory-only content, they are identical. Therefore it is not clear, per compiler, which type of READ is the default (when not explicit) or when it is the default (per file). So I always make it explicit:
READ PRODUCTEN KEY IS WS-PRODUCTID
I would then use the FILE STATUS field to determine whether the key was read (00 in the status) or not found (23) or something else (something else).
NOTE: This Answer as a resolution to your problem only works if everything is as you have described. Further information may invalidate this Answer as a Resolution.
The Answer does work as a generally clearer (therefore better) way to code your COBOL program.
Turns out to have been a suspected corrupted file. This may have caused a disparity between INVALID KEY and FILE STATUS, but in the normal course of events that is not going to happen. It is the only thing which fits all the evidence, but this is an exceptional case, perhaps not able to reproduce without the exact-same file corruption and clutching at this straw in a general case for why a given program is not working is probably the first refuge of a scoundrel.
I am getting compilation error as,
A "COPY" statement with "REPLACING" phrase was found within a nested "COPY".
This is our compilation setting that we can not use REPLACING verb in nested copy.
We have one copybook which is having multiple copy statements with replacing verb.
Can anyone help me to resolve this error?
Nesting COPYBOOKS in COBOL is a bit of a trick. In general
you may nest copybooks only if they do not
contain the REPLACING phrase and do not cause recursion.
Suppose you had the following two copybooks:
COPYBOOK ABC
01 :A:-VAR-A1 PIC X.
01 :A:-VAR-A2 PIC X.
COPY XYZ REPLACING ==:A:== BY ==B==.
and
COBPYOOK XYZ
01 :A:-VAR-X1 PIC X.
01 :A:-VAR-X2 PIC X.
The nesting in COPYBOOK ABC is not allowed because it contains a REPLACING phrase.
However, you can do the following. Drop RELACING from COPYBOOK ABC so
it becomes:
COPYBOOK ABC
01 :A:-VAR-A1 PIC X.
01 :A:-VAR-A2 PIC X.
COPY XYZ.
Now include COPYBOOK ABC into your source program as follows:
REPLACE ==:A:== BY ==B==.
COPY ABC.
REPLACE OFF.
The REPLACE directive causes all occurances of :A: to
be replaced by B until a REPLACE OFF directive is encountered, and these
replacements occur after all COPY directives have been actioned. The net
result of the above statements would be:
01 B-VAR-A1 PIC X. <== from ABC
01 B-VAR-A2 PIC X. <== from ABC
01 B-VAR-X1 PIC X. <== Nested copy of XYZ from ABC
01 B-VAR-X2 PIC X. <== Nested copy of XYZ from ABC
This is the only 'legal' way of performing replacements to nested copybooks
in COBOL that I am aware of.
While Neal's answer may be correct for the Cobol variant he and the OP are using, it is worth noting that some Cobol compilers will allow nested COPY ... REPLACING statements.
Indeed we have many instances where we use this in our code.
For example, I have many Cobol module programs that include a copymember in the following way:
COPY 'uwxxxxsel.prg' REPLACING
LEADING "xxxxSEL" BY "AREASEL".
and the 'uwxxxxsel.prg' copymember contains the following COPY of another file as follows:
COPY 'uwf8list.prg'
REPLACING LEADING 'XXXXXX-F8-' BY 'SELECT-F8-'
==FUNCTION-KEY== BY ==F8-FUNCTION-KEY==.
and this works fine.
I use this as a kind of 'inheritance' structure for common functionality.
There is one gotcha, though, with our Compiler. The higher level REPLACING does not continue after the nested REPLACING, but as long as the nested REPLACING is the last thing in the copymember, it is fine.