问题
We are supposed to form an array of names that occur 108 times. We are supposed to have name 1-54 in a left column and names 55-108 in a right column. After there have been 108 names for one page, we initialize our array and start over again. The output for my code is showing names 1-54 printed and, instead of being on the same page and beside names 1-54, names 55-108 in the right column but after names 1-54. Any thoughts would be greatly appreciated.
Here is some of my code:
PERFORM UNTIL ARE-THERE-MORE-RECORDS = 'NO '
READ NAMELIST-FILE-IN
AT END
MOVE 'NO ' TO ARE-THERE-MORE-RECORDS
NOT AT END
PERFORM 200-PROCESS-ONE-RECORD
END-READ
END-PERFORM
CLOSE NAMELIST-FILE-IN
CLOSE NAMELIST-FILE-OUT
STOP RUN.
200-PROCESS-ONE-RECORD.
ADD 1 TO NAME-SUB
MOVE NAME-IN TO NAME-1 (NAME-SUB)
PERFORM 220-MOVE-RECORDS.
220-MOVE-RECORDS.
IF NAME-SUB <= 54
MOVE NAME-1 (NAME-SUB) TO LEFT-LABEL
MOVE SPACES TO RIGHT-LABEL
END-IF
IF NAME-SUB >= 55
MOVE NAME-1 (NAME-SUB) TO RIGHT-LABEL
MOVE SPACES TO LEFT-LABEL
END-IF
MOVE DETAIL-LINE TO NAMELIST-RECORD-OUT
WRITE NAMELIST-RECORD-OUT
AFTER ADVANCING 1 LINE
IF NAME-SUB >= 108
MOVE SPACES TO DETAIL-LINE
MOVE ZERO TO NAME-SUB
PERFORM 300-WRITE-HEADING
END-IF.
I have coded all the proper WORKING-STORAGE entries to accommodate the information. Do you know if there is something wrong with the way I am writing the detail-line or is it the way I am processing my data?
回答1:
Your logic is wrong. Lets say (just to make things easy) you have 216 names, you will need to read in 108 of them and store them in your NAME-1 array.
Then you can loop over the 54 lines placing NAME-1[n] into LEFT-LABEL and NAME-1[n+54] into RIGHT-LABEL, Then move your detail-line and write to output; repeating for lines n = 1 <= 54
Now read in your next 108 lines and repeat. So two loops; Read 108 names, print 54 lines.
Obviously you will need to guard for your remainder, ie if you don't have exactly a multiple of 108 names, something like
if n <= name-sub
move NAME-1[n] to LEFT-LABEL
else
move spaces to LEFT-LABEL
endif
if n+54 <= name-sub
move NAME-1[n+54] to RIGHT-LABEL
else
move spaces to RIGHT-LABEL
endif
I realise you will have to set the variables properly (n+54 is not proper cobol) and sorry for the mix of case, but long time ago writing COBOL and used to lower case now. ;)
回答2:
If I understand correctly, this should be close to what you want
220-MOVE-RECORDS.
IF NAME-SUB >= 108
perform varing i from 1 to 54
MOVE NAME-1 (NAME-SUB) TO LEFT-LABEL
compute ip54 = i + 54
MOVE NAME-1 (ip54) TO RIGHT-LABEL
WRITE NAMELIST-RECORD-OUT
from DETAIL-LINE
AFTER ADVANCING 1 LINE
end-perform
MOVE SPACES TO DETAIL-LINE
MOVE ZERO TO NAME-SUB
PERFORM 300-WRITE-HEADING
END-IF.
Note: many Cobol compilers allow lower case
回答3:
You should always have error checking for all your IO.
A one-file-in-one-file-out can always look like this:
open input
check status
open output
check status
process file until end
close input
check status
close output
check status
process file
read input
check staus
do what is needed
write output
check status
Better is like this:
open input
check status
open output
check status
*priming read*
process file until end
close input
check status
close output
check status
process file
do what is needed
write output
check status
read input
check staus
The "priming read" deals with the first record on the file (if any). You can neatly handle an "empty file" without having to "confuse" your main logic or having to differentiate between two different types of "end of file" elsewhere. The read now at the end of "process file" removes the somewhat tortuous "AT END/NOT AT END".
For the example, you only need 54 elements in your table. When processing a record for the "right" side of the page, you can take the first from the "left" and do the line immediately.
Use 88s rather than literals for tests.
Don't do your "headings" at the end of a page, as if there are no more records to process, you will have a "blank page" following your headings.
If the write of your print line is in a paragraph, that paragraph can be used check whether a heading is needed, with a "line count" which has an initial value of 54.
With the 108-element approach where you are printing a page-at-a-time, do the headings there, at the top.
The is no need to set things to initial values if the data is never used before it is set to something else.
You've adopted the "minimal full-stop/period" approach to procedure code, which is good - how about putting that necessary final period on a line of its own?
PERFORM 220-MOVE-RECORDS.
becomes
PERFORM 220-MOVE-RECORDS
.
Only use >= or<= when the values can logically exceed the maximum. Yours never can, so use EQUAL TO. Yes, if it exceeds, you get a Big Fat Loop. But that is better than "working" when something unexpected has happened. If you want to test > for exceeding and then failing with a diagnostic message, that's OK. Some compilers allow "bounds checking" of table accesses, if you are using that, you'd not even need the extra check.
回答4:
It would have been helpful to see your Working Storage definitions as well as the code. It is hard to understand one without the other.
At any rate, what you are describing is a fairly "standard" sort of problem to which there are several possible solutions. What follows is an outline of one possible approach.
Start with a data structure... Working Storage:
01 WS-PAGE-BUFFER.
02 WS-LINE OCCURS 54 TIMES.
03 WS-NAME PIC X(40) OCCURS 2 TIMES.
The above working storage describes one page of output. The page contains 54 lines. Each line contains two names. Next you need a few counters...
01.
02 WS-LINE-CNTR PIC S9(4) COMP.
02 WS-NAME-CNTR PIC S9(4) COMP.
Two problems to solve:
- Filling the page in the proper sequence
- Printing the page with appropriate headings/trailers
Something else to keep in mind when solving these problems is that you need to cover several scenaios with respect to inputs: No input, input fits exactly to some number of ouput pages and input partly fills an output page. So whatever you do, all of these situations need to sort themselves out in a "natural" way. Also, there is generally some sort of pre/post amble stuff to work out (eg. initializations, open files, close files etc.).
One more thing... Always declare a FILE-STATUS for your Input/Output files to capture errors and end-of-file conditions. The algorithm below assumes you have done that (end-of-file status is generally '10')
Skeleton algorithm.
MAINLINE
PERFORM INITIALIZE-PAGE
Open input file (check status etc...)
Open output file (check status etc...)
Read first line from file (check for errors/end of file etc...)
PERFORM UNTIL INPUT-FILE-STATUS NOT = ZERO /* read until eof/error
IF WS-LINE-CNTR = 54 AND WS-NAME-CNT = 2 /* check for full page.
PERFORM OUTPUT-PAGE
END-IF
ADD +1 TO WS-LINE-CNTR
IF WS-LINE-CNTR > 54
MOVE +1 TO WS-LINE-CNTR /* Start next column...
ADD +1 TO WS-NAME-CNTR /* Increment column
END-IF
MOVE input-record TO WS-NAME (WS-LINE-CNTR, WS-NAME-CNTR)
Read next line from input file
END-PERFORM
IF INPUT-FILE-STATUS = '10' AND WS-LINE-CNTR > ZERO
PERFORM OUTPUT-PAGE /* force the last page to print
END-IF
close input file
close output file
GOBACK /* done
.
INITIALIZE-PAGE.
MOVE SPACE TO WS-PAGE-BUFFER /* Blank page (ie. SPACES)
MOVE ZERO TO WS-LINE-CNTR /* Top of page
MOVE +1 TO WS-NAME-CNTR /* First column of page
.
OUTPUT-PAGE.
Ouput page headers...
PERFORM VARYING WS-LINE-CNTR FROM 1 BY 1
UNTIL WS-LINE-CNTR > 54
write WS-LINE (WS-LINE-CNTR) to output file (check status etc...)
END-PERORM
Output page trailers...
PERFORM INITIALIZE-PAGE /* Start a fresh page...
.
I have left plenty of "blank spots" to be filled in and I will admit there are other more elegant ways to accomplish what you are trying to do, but this should get you started.
来源:https://stackoverflow.com/questions/14679229/printing-in-two-columns