Search code examples
cobolgnucobol

Output is wrong


Written my program but I cannot get the right output as needed Below is my code and my input with the output.

Also does my procedure program make sense or should I revise it, it seems it makes sense but after looking at different books I'm not sure anymore.

FD INPUT-FILE.
   01 INPUT-RECORD.
       05 EXCUSE-NUMBER                     PIC 9(02).
          88 VALID-EXCUSE                   VALUE 1 THRU 10.
       05 FILLER                            PIC X(03).
       05 NUMBER-TIMES-USED                 PIC 9(02).
       05 FILLER                            PIC X(73).

   FD REPORT-FILE.
   01 REPORT-RECORD                         PIC X(80).

   FD ERROR-FILE.
   01 ERROR-RECORD                          PIC X(80).

   WORKING-STORAGE SECTION.
  ******************************************************************
  *  DEFINES PROCESSINGVARIABLES AND OUTPUT LINES                  *
  ******************************************************************
   01 WS-AREA.
       05 WS-IF-STATUS                      PIC X(02).
       05 WS-OF-STATUS                      PIC X(02).
       05 WS-EF-STATUS                      PIC X(02).
       05 WS-END-OF-FILE                    PIC X(01) VALUE "N".
           88 AT-WS-END-OF-FILE                       VALUE "Y".
       05 WS-INVALID-RECORD                 PIC X(01) VALUE "N".
       05 WS-LINE-NUMBER                    PIC 9(03) VALUE 0.
       05 WS-MOST-USED-EXCUSE               PIC 9(02) VALUE 0.
       05 WS-EXCUSE                         PIC 9(02) VALUE 0.
       05 WS-EXCUSE-TOTAL                   PIC 9(04).

   01 BLANK-LINE.
       05                                   PIC X(80).

  ******************************************************************
  * (THIS IS WHERE THE HEADING ON THE PRINT OUT SHEET GOES)        *
  ******************************************************************

   01 HEADING-LINE-1.
       05 FILLER                            PIC X(14) VALUE SPACES.
       05 FILLER                            PIC X(37)
                VALUE "TEN MOST OUTRAGEOUS HOME-WORK EXCUSES".

   01 HEADING-LINE-2.
       05 FILLER                           PIC X(11).
       05 FILLER                           PIC X(06) VALUE "RECORD".
       05 FILLER                           PIC X(08).
       05 FILLER                           PIC X(05) VALUE "IMAGE".

  ******************************************************************
  * DETAIL-LINE COMMENTS.                                          *
  * on the detail line we are writing out the Data information     *
  * in particular, when we write out the line-number, error        *
  * excuse number, excuses used, and how many times used           *
  * Detail summary will be reported out                            *
  * Stars will be Display underneath bad data.                     *
  *****************************************************************
   01 DETAIL-LINE.
       05 FILLER                           PIC X(03).
       05 DL-EXCUSE-NUMBER                 PIC 9(02).
       05 FILLER                           PIC X(03).
       05 DL-EXCUSE-USED                   PIC X(51).
       05 FILLER                           PIC X(03).
       05 DL-AMOUNT-USED                   PIC ZZ9.

   01 DETAIL-LINE-ERROR-1.
       05 FILLER                           PIC X(08) VALUE SPACES.
       05 DLE-LINE-NUMBER                  PIC ZZ9.
       05 FILLER                           PIC X(09) VALUE SPACES.
       05 DLE-ERROR                        PIC X(16).

   01 DETAIL-LINE-ERROR-2.
       05 FILLER                           PIC X(20) VALUE SPACES.
       05 DLE-EXCUSE-NUMBER                PIC X(02) VALUE SPACES.
       05 FILLER                           PIC X(03) VALUE SPACES.
       05 DLE-EXCUSE                       PIC X(02) VALUE SPACES.

   01 DETAIL-TOTAL.
       05 FILLER                           PIC X(41) VALUE SPACES.
       05 FILLER                           PIC X(20)
                VALUE "TOTAL EXCUSES USED =".
       05 DT-TOTAL                         PIC ZZZ9 VALUE ZERO.

   01 DETAIL-TOTAL-MOST-USED.
       05 FILLER                           PIC X(34) VALUE SPACES.
       05 FILLER                           PIC X(27)
                VALUE "EXCUSE USE THE MOST TIMES =".
       05 DTMU-HIGH                        PIC ZZZ9.

  ******************************************************************
  * This is where we hard code the excuses used with the table     *
  * from the input file.                                           *
  ******************************************************************
   01 TABLE-EXCUSES-1.
       05 PIC X(51)
       VALUE "JOHN CONVINCE ME TO CONVERT TO LINUX".
       05 PIC X(51)
       VALUE "BEACUSE OF SECURITY REASON I CAN'T CONFIRM NOR DENY".
       05 PIC X(51)
       VALUE "BECAUSE THE HOSPITAL DOESN'T HAVE WIFI'".
       05 PIC X(51)
       VALUE "AFTER INSTALLING LINUX MY SYSTEM CRASHED".
       05 PIC X(51)
       VALUE "WHAT WAS THE QUESTION AGAIN".
       05 PIC X(51)
       VALUE "ARE YOU SURE, I REMEBER TURNING IT IN".
       05 PIC X(51)
       VALUE "I'M INVOKING MY 5TH AMENDMENT RIGHT".
       05 PIC X(51)
       VALUE "LINUX MADE ME CRAZY I THREW MY COMPUTER".
       05 PIC X(51)
       VALUE "SOMEONE STOLED MY BACKPACK".
       05 PIC X(51)
       VALUE "BEACUSE OF SECURITY REASON I CAN'T CONFIRM NOR DENY".

   01 TABLE-EXCUSES-2 REDEFINES TABLE-EXCUSES-1.
       05 TEN-EXCUSES OCCURS 10 TIMES      PIC X(51).

   01 TABLE-EXCUSES-COUNTER.
       05 TABLE-EXCUSES-COUNT OCCURS 10 TIMES PIC 9(03).


   PROCEDURE DIVISION.
  ******************************************************************
  * Finally - where the real work gets done                        *
  * it is divided into paragraphs (or modules) generally called    *
  * from the main controlling module (here 1000-MAIN-CONTROL).     *
  * 1000-Main be the control module,                               *
  * 2000-Initialize                                                *
  * 3000-Process                                                   *
  * 4000-Finish                                                    *
  ******************************************************************
   1000-MAIN.
       OPEN INPUT INPUT-FILE
            OUTPUT REPORT-FILE, ERROR-FILE

       PERFORM 2000-INITIALIZE

       PERFORM UNTIL AT-WS-END-OF-FILE

            READ INPUT-FILE
                AT END MOVE "Y" TO WS-END-OF-FILE
                NOT AT END PERFORM 3000-PROCESS
           END-READ
       END-PERFORM


       PERFORM 4000-FINISH
           VARYING WS-EXCUSE
           FROM 1 BY 1
           UNTIL WS-EXCUSE > 10

       MOVE WS-MOST-USED-EXCUSE TO DTMU-HIGH
       MOVE WS-EXCUSE-TOTAL TO DT-TOTAL
       WRITE REPORT-RECORD FROM BLANK-LINE
       WRITE REPORT-RECORD FROM DETAIL-TOTAL
       WRITE REPORT-RECORD FROM BLANK-LINE
       WRITE REPORT-RECORD FROM DETAIL-TOTAL-MOST-USED.

       CLOSE INPUT-FILE REPORT-FILE ERROR-FILE
       STOP RUN.

   2000-INITIALIZE.
       INITIALIZE WS-EXCUSE
       INITIALIZE TABLE-EXCUSES-COUNTER
       WRITE ERROR-RECORD FROM HEADING-LINE-2
       WRITE ERROR-RECORD FROM BLANK-LINE
       WRITE REPORT-RECORD FROM HEADING-LINE-1
       WRITE REPORT-RECORD FROM BLANK-LINE.

   3000-PROCESS.
       MOVE "N" TO WS-INVALID-RECORD
       ADD 1 TO WS-LINE-NUMBER

       IF NOT VALID-EXCUSE
       MOVE INPUT-RECORD TO DLE-ERROR
       MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
       MOVE "Y" TO WS-INVALID-RECORD
       MOVE ALL "*" TO DLE-EXCUSE-NUMBER

       END-IF.

       IF WS-INVALID-RECORD = "N"
       INSPECT EXCUSE-NUMBER REPLACING LEADING SPACES BY ZERO
       IF EXCUSE-NUMBER IS NUMERIC
          ADD EXCUSE-NUMBER TO TABLE-EXCUSES-COUNT(EXCUSE-NUMBER)
       IF TABLE-EXCUSES-COUNT(EXCUSE-NUMBER) >
          WS-MOST-USED-EXCUSE
       MOVE TABLE-EXCUSES-COUNT(EXCUSE-NUMBER)
                                             TO WS-MOST-USED-EXCUSE
       END-IF

          ADD NUMBER-TIMES-USED TO WS-EXCUSE-TOTAL

       ELSE

       MOVE "Y" TO WS-INVALID-RECORD
       MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
       MOVE INPUT-RECORD TO DLE-ERROR
       MOVE ALL "*" TO DLE-EXCUSE

            END-IF

       END-IF.

       IF WS-INVALID-RECORD = "Y"
       WRITE ERROR-RECORD FROM DETAIL-LINE-ERROR-1
       WRITE ERROR-RECORD FROM DETAIL-LINE-ERROR-2
       MOVE SPACES TO DETAIL-LINE-ERROR-1
       MOVE SPACES TO DETAIL-LINE-ERROR-2

       END-IF.

   4000-FINISH.
       MOVE WS-EXCUSE TO DL-EXCUSE-NUMBER
       MOVE TEN-EXCUSES(WS-EXCUSE) TO DL-EXCUSE-USED
       MOVE TABLE-EXCUSES-COUNT(WS-EXCUSE) TO DL-AMOUNT-USED
       WRITE REPORT-RECORD FROM DETAIL-LINE
       WRITE REPORT-RECORD FROM BLANK-LINE
       MOVE SPACES TO DETAIL-LINE.

My output is as follow then follow by what it should be.

RECORD        IMAGE

      3         0r4000700     03
                     **
     12         125999999     12
                **
     21         125000899     21
                **
     23         A01001111     23
                **                   

Should be:

RECORD        IMAGE

      3         0r4000700     03
                **
      6         074000Q00     06
                     **
     12         125999999     12
                **
     21         125000899     21
                **
     23         A01001111     23
                **

Solution

  • You have two problems which is causing record number three to not appear with the correct error and record number six not to appear as an error at all.

    I've indented your code to allow you to better see what is going on.

    As usual, the compiler doesn't care, doesn't take note of indentation, so it is for humans. So do it. Indent. Often you'll see some of your own errors just by doing that.

       IF NOT VALID-EXCUSE
           MOVE INPUT-RECORD TO DLE-ERROR
           MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
           MOVE "Y" TO WS-INVALID-RECORD
           MOVE ALL "*" TO DLE-EXCUSE-NUMBER
       END-IF
    
       IF WS-INVALID-RECORD = "N"
           INSPECT EXCUSE-NUMBER REPLACING LEADING SPACES BY ZERO
           IF EXCUSE-NUMBER IS NUMERIC
               ADD EXCUSE-NUMBER TO TABLE-EXCUSES-COUNT(EXCUSE-NUMBER)
               IF TABLE-EXCUSES-COUNT(EXCUSE-NUMBER) >
                   WS-MOST-USED-EXCUSE
                   MOVE TABLE-EXCUSES-COUNT(EXCUSE-NUMBER)
                                             TO WS-MOST-USED-EXCUSE
               END-IF
               ADD NUMBER-TIMES-USED TO WS-EXCUSE-TOTAL
           ELSE
               MOVE "Y" TO WS-INVALID-RECORD
               MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
               MOVE INPUT-RECORD TO DLE-ERROR
               MOVE ALL "*" TO DLE-EXCUSE
           END-IF
       END-IF
    

    If we take record three first.

    Your range-test on the 88 (good to use 88s, do it more) is this, in hexadecimal:

    X'3031' through X'3130'. 
    

    This will work if the field is already known to be NUMERIC, but otherwise, since numbers appear before letters in ASCII, a whole slew of stuff you don't want gets treated as "valid". The value of 12 are rejected because they are large than 10 (X'3130'). Any letter, preceded by a zero, will be treated as valid, as will any control-code or any remaining value that happens to fit in the huge range of non-numeric values.

    As Bruce Martin indicated, you need to know that the field is NUMERIC before applying that test.

       IF EXCUSE-NUMBER NUMERIC
       AND NOT VALID-EXCUSE
           MOVE INPUT-RECORD TO DLE-ERROR
           MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
           MOVE "Y" TO WS-INVALID-RECORD
           MOVE ALL "*" TO DLE-EXCUSE-NUMBER
       END-IF
    

    That's become a little difficult for humans to read (the compiler doesn't mind). Bruce's suggestion for simplification (positive checks and CONTINUE with ELSE to catch the bad data) is a good one:

       IF  ( EXCUSE-NUMBER NUMERIC )
       AND ( VALID-EXCUSE ) 
           [all the good data goes here]
           CONTINUE
       ELSE
           [leaving all the bad data here, ie both not numeric and
            numeric but not in range]
           MOVE INPUT-RECORD TO DLE-ERROR
           MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
           MOVE "Y" TO WS-INVALID-RECORD
           MOVE ALL "*" TO DLE-EXCUSE-NUMBER
       END-IF
    

    Note: You need the NUMERIC-plus-range when you are validating data. Once good data is in your system, range-checks will work "as expected" as there will be no intervening non-numeric data to spoil the plot.

    You also get to do both types errors for EXCUSE-NUMBER at once, simplifying the following IF.

    If you can have a leading blank in either field, you need to deal with that before any checking. You don't need to use INSPECT.

    With a two-byte field just REDEFINES so you can give the entire field as alpha-numeric a name, and first byte a name. Put 88s on those with a value of space. If the 88 is true, set that to zero (whole field, or byte, two tests):

    IF 88-level
        MOVE ZERO TO name-you've-given 
    END-IF
    

    So far, record three has been treated as valid. Now it gets into your nested-IF. It EXCUSE-NUMBER is not NUMERIC, so hits the ELSE, where you have coding for the second field, . Because you didn't indent, this was obscured.

    Record three has been rejected "by accident".

    In the nested-IF, you meant to check NUMBER-TIMES-USED for NUMERIC.

    Which explains why record six does not appear as an error, because it's only fault is NUMBER-TIMES-USED not being NUMERIC, which your program currently doesn't notice.

    You've also erroneously added EXCUSE-NUMBER instead of NUMBER-TIMES-USED.

    IF WS-INVALID-RECORD = "N"
        [stuff for leading space]
        IF NUMBER-TIMES-USED IS NUMERIC
            ADD NUMBER-TIMES-USED TO TABLE-EXCUSES-COUNT(EXCUSE-NUMBER)
            IF TABLE-EXCUSES-COUNT(EXCUSE-NUMBER) >
                WS-MOST-USED-EXCUSE
                MOVE TABLE-EXCUSES-COUNT(EXCUSE-NUMBER)
                                          TO WS-MOST-USED-EXCUSE
            END-IF
            ADD NUMBER-TIMES-USED TO WS-EXCUSE-TOTAL
        ELSE
            MOVE "Y" TO WS-INVALID-RECORD
            MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
            MOVE INPUT-RECORD TO DLE-ERROR
            MOVE ALL "*" TO DLE-EXCUSE
        END-IF
    END-IF
    

    Be aware that the size of TABLE-EXCUSES-COUNT is greater than the size of WS-MOST-USED-EXCUSE. If you keep it like that, you will get unexpected behaviour when you have more than 99 of the same type of excuse.

    Your nested-IF is a bit tortuous, and you have some repetition. Here's some simplification:

        IF  ( EXCUSE-NUMBER NUMERIC )
        AND ( VALID-EXCUSE ) 
            PERFORM CHECK-NUMBER-TIMES-USED
        ELSE
            PERFORM SET-STANDARD-REJECTION
            MOVE ALL "*" TO DLE-EXCUSE-NUMBER
        END-IF
    
    ...
    
    CHECK-NUMBER-TIMES-USED.
        IF 88-level-first-byte-space
            MOVE ZERO TO name-you've-given-the-first-byte
        END-IF
        IF 88-level-field-space
            MOVE ZERO TO field
        END-IF
        IF NUMBER-TIMES-USED IS NUMERIC
            ADD NUMBER-TIMES-USED TO TABLE-EXCUSES-COUNT ( EXCUSE-NUMBER )
                                     WS-EXCUSE-TOTAL
            PERFORM CHECK-HIGHEST-EXCUSE-COUNT
        ELSE
            PERFORM SET-STANDARD-REJECTION
            MOVE ALL "*" TO DLE-EXCUSE
        END-IF
    
    
    SET-STANDARD-REJECTION.
        MOVE INPUT-RECORD TO DLE-ERROR
        MOVE WS-LINE-NUMBER TO DLE-LINE-NUMBER
        MOVE "Y" TO WS-INVALID-RECORD
        .
    
    CHECK-HIGHEST-EXCUSE-COUNT.
        IF TABLE-EXCUSES-COUNT ( EXCUSE-NUMBER ) 
            GREATER THAN WS-COUNT-OF-MOST-USED
            MOVE TABLE-EXCUSES-COUNT ( EXCUSE-NUMBER )
                                      TO WS-COUNT-OF-MOST-USED
            MOVE EXCUSE-NUMBER        TO WS-EXCUSE
        END-IF
        .
    

    There's at least one more simplification when you get to the totals, but see how that goes.