How do you grab a string in COBOL from a file when the position is unknown?

Tom Flynn picture Tom Flynn · Aug 7, 2013 · Viewed 7.4k times · Source

I'm new to the site as well as COBOL. I am trying to write a program that reads in an 80 byte file, and finds a certain string and grabs another string that is positioned right after that. The only issue I'm having with this is that the starting position of the string is not always in the same byte throughout the file. For example, the string I am trying to find below is the LENGTH(#####) string that appears twice throughout the file:

LENGTH(14909135) FILEID(DD:EDIREC) MSGDATE(130723) MSGDATELONG(20130723)
MSGTIME(091053) MSGSEQO(001390) MSGNAME(00008557) MSGSEQNO(00001)
SESSIONKEY(XXXXXXXX) DELIMITED(E) SYSNAME(XXXXX-XX) SYSLEVEL(XXXX) TIMEZONE(L)
DATATYPE(E) EDITYPE(XXX) SENDERFILE(#####) RECFM(????) RECLEN(#) RECDLM(E)
UNIQUEID(XXXXXXXX) SYSTYPE(##) SYSVER(#);
RECEIVED ACCOUNT(XXXX) USERID(XXXXXXXX) CLASS(#E2) CHARGE(3) LENGTH(14911043)
FILEID(DD:EDIREC) MSGDATE(130723) MSGDATELONG(20130723) MSGTIME(093045)
MSGSEQO(001392) MSGSEQNO(00000) SESSIONKEY(XXXXXXXX) DELIMITED(C)
SYSNAME(XXXXX-XX) SYSLEVEL(XXXX) TIMEZONE(L) DATATYPE(E) EDITYPE(UNFORMATTED)
SENDERFILE(XXXXXXXXXXXXX) RECFM(????) RECLEN(0) RECDLM(C) UNIQUEID(XXXXXXXX)
SYSTYPE(24) SYSVER(5);

Notice the two LENGTH(#####) strings. The below code manages to count the amount of times the length string appears as well as grab the final length string count (what I really want, the numbers within the length string), but only when they are in these two positions:

    WORKING-STORAGE SECTION.

    01 WS-INPUT-RECORD   PIC X(80).

    01 WS-STRINGS.

       05 LENGTH-STRING      PIC X(7) VALUE 'LENGTH('.

    01 WS-COUNTERS.

       05 WS-MSG-COUNT  PIC 9(11).

    01 WS-CHAR-TOTALS.

       05 CHAR-TOTAL  PIC 9(11) VALUE ZEROS.

       05 TMP-TOTAL  PIC X(11) VALUE ZEROS.

    ......

    PROCEDURE DIVISION.

    2200-GET-MSG-TOTAL.

        INSPECT WS-INPUT-RECORD
        TALLYING WS-MSG-COUNT FOR ALL LENGTH-STRING.

    2300-CHAR-TOTAL.

        IF WS-INPUT-RECORD(1:7) = LENGTH-STRING

           MOVE WS-INPUT-RECORD(8:9) TO TMP-TOTAL

           UNSTRING TMP-TOTAL DELIMITED BY ')'
           INTO CHAR-TOTAL

        END-IF

        IF WS-INPUT-RECORD(61:7) = LENGTH-STRING

           MOVE WS-INPUT-RECORD(68:9) TO TMP-TOTAL

           UNSTRING TMP-TOTAL DELIMITED BY ')'
               INTO CHAR-TOTAL

        END-IF

The code works great for the two positions shown in the example input above. But it won't work if LENGTH(####) ends up in any other byte position. Other than coding 80 IF statements to check for every byte in the file for the string, is there an easier way to go about getting those values inside of the length parens? I've checked a lot of other posts and I've thought about using pointers or tables but I can't quite seem to figure it out.

Answer

Bill Woodger picture Bill Woodger · Aug 7, 2013

Use INSPECT to establish that LENGTH( is on the current record.

Only if present, do the following:

UNSTRING using LENGTH( as a delimiter with two receiving fields.

UNSTRING second receiving field delimited by ) leaving you with the number.

For example:

01  delimiting-field PIC X(7) VALUE "LENGTH(".
01  desitnation-field-1 PIC X.
01  destination-field-2 PIC X(18) JUST RIGHT.

UNSTRING source-field DELIMITED BY delimiting-field INTO desitnation-field-1
                                                         destination-field-2

Abandon destination-field-1. Use destination-field-2 for input to the second UNSTRING.

Use meaningful names, rather than those I have shown to illuminate the example.

So,

    01  WS-INPUT-RECORD                     PIC X(80). 
    01  NUMBER-OF-LENGTHS            BINARY PIC 9(4). 
    01  DELIMITER-COUNT              BINARY PIC 9(4). 
        88  NO-DELIMITERS                   VALUE ZERO. 
        88  ONE-DELIMITER                   VALUE 1. 
    01  LENGTH-OPEN-PAREN                   PIC X(7) 
                                            VALUE "LENGTH(". 
    01  DATA-TO-IGNORE                      PIC X. 
    01  DATA-WITH-LENGTH-VALUE              PIC X(80). 
    01  CLOSING-PAREN                       PIC X VALUE ")". 
    01  VALUE-OF-LENGTH-AN                  PIC X(18) JUST RIGHT.

   THE-STUFF. 
       SET NO-DELIMITERS            TO TRUE 
       INSPECT WS-INPUT-RECORD      TALLYING DELIMITER-COUNT 
                                     FOR ALL LENGTH-OPEN-PAREN
       EVALUATE TRUE 
           WHEN NO-DELIMITERS 
               CONTINUE 
           WHEN ONE-DELIMITER 
               PERFORM              GET-THE-DATA 
           WHEN OTHER 
               PERFORM              OH-DEAR-MORE-THAN-ONE 
       END-EVALUATE 
       . 
   GET-THE-DATA. 
       UNSTRING WS-INPUT-RECORD     DELIMITED BY 
                                    LENGTH-OPEN-PAREN 
           INTO                     DATA-TO-IGNORE 
                                    DATA-WITH-LENGTH-VALUE 
       UNSTRING DATA-WITH-LENGTH-VALUE 
                                    DELIMITED BY CLOSING-PAREN
           INTO                     VALUE-OF-LENGTH-AN 
       DISPLAY "THIS IS WHAT WE FOUND" 
       DISPLAY ">" 
               VALUE-OF-LENGTH-AN 
               "<" 
       . 
   OH-DEAR-MORE-THAN-ONE. 
       DISPLAY "THE FOLLOWING LINE HAS MORE THAN ONE LENGTH(" 
       DISPLAY ">" 
               WS-INPUT-RECORD 
               "<" 
       . 

The technique with the INSPECT to see if the "string" is present can be applied to the other solution accepted so that only if the line contains the value desired is it "searched".