COBOL Subscript Variable Length String

173 Views Asked by At

I'm learning COBOL and am attempting to read an unformatted file with C style file i/o (reading x bytes at a time). I've so far succeeded in reading the file one byte at a time and displaying the individual characters immediately, but I've been unable to store the contents of the file in a variable.

The code that I've tried is this:

        IDENTIFICATION DIVISION.
        PROGRAM-ID. FILEREADER.

        ENVIRONMENT DIVISION.
        INPUT-OUTPUT SECTION.
        FILE-CONTROL.
            SELECT FILE-TO-READ
            ASSIGN USING INPUT-FILE-NAME 
            ORGANIZATION IS SEQUENTIAL
            ACCESS IS SEQUENTIAL.

        DATA DIVISION.
        FILE SECTION.
        FD FILE-TO-READ.
            01 CHAR PIC X(1).

        WORKING-STORAGE SECTION.
        01 FILE-DATA-POINTER USAGE IS POINTER.
        01 FILE-DATA-HOLDER PIC X BASED.
        01 INPUT-DATA.
            05 INPUT-FILE-NAME PIC X(100). 
            05 INPUT-FILE-CHARACTERS PIC 9(18).
        01 MISC-DATA.
           05 EOF PIC X(1).
           05 STRING-INDEX PIC 9(18) USAGE IS COMP.
        PROCEDURE DIVISION.
        MAIN.
            PERFORM GET-INPUT.
            PERFORM ALLOCATE-STORAGE.
            PERFORM READ-FILE.
            PERFORM DISPLAY-FILE.
            PERFORM CLEAN-UP.
            STOP RUN.

        GET-INPUT.
            DISPLAY "Please enter the name of the file (maximum 100 char
      -     "acters)."
            ACCEPT INPUT-FILE-NAME.

            DISPLAY "Please enter the number of characters in your file
      -     "(the number of bytes)."
            ACCEPT INPUT-FILE-CHARACTERS.

        ALLOCATE-STORAGE.
            ALLOCATE INPUT-FILE-CHARACTERS CHARACTERS RETURNING
            FILE-DATA-POINTER SET ADDRESS OF FILE-DATA-HOLDER TO
            FILE-DATA-POINTER.
            
            IF ADDRESS OF FILE-DATA-POINTER = NULL
                DISPLAY "System error when allocating RAM. Exiting..."
                STOP RUN
            END-IF.

        READ-FILE.
            INITIALIZE STRING-INDEX.
            MOVE 'N' TO EOF.
            MOVE 1 TO STRING-INDEX.
            OPEN INPUT FILE-TO-READ.
                PERFORM UNTIL EOF = 'Y'
                    READ FILE-TO-READ 
                        AT END MOVE 'Y' TO EOF
                        NOT AT END MOVE CHAR TO FILE-DATA-HOLDER
                        (STRING-INDEX:1)

                        ADD 1 TO STRING-INDEX    
                    END-READ
                END-PERFORM.
           CLOSE FILE-TO-READ.
       
        DISPLAY-FILE.
            DISPLAY FILE-DATA-HOLDER.
       
        CLEAN-UP.
            FREE FILE-DATA-POINTER.

When compiling this program I get the warning:

file_reader.cbl:64: warning: suspicious reference-modification: always using max. length [-Wothers]
   62 |                     READ FILE-TO-READ
   63 |                         AT END MOVE 'Y' TO EOF
   64 >                         NOT AT END MOVE CHAR TO FILE-DATA-HOLDER
   65 |                         (STRING-INDEX:1)

The program outputs the following:

Please enter the name of the file (maximum 100 characters).
test.txt         <------ User input
Please enter the number of characters in your file (the number of bytes).
4                <------ Also user input
L

In this example, the text file contains the phrase "Lorem Ipsum" ASCII encoded.

After some research I figured out that this is because strings can't be subscripted/indexed like in C or Python. I then tried to use an array (table) of characters instead of a string, but this didn't work because you apparently can't dynamically allocate tables in COBOL, so now I'm back at square one.

To summarize, I need to find a way to store an entire file in a dynamically allocated variable in standard COBOL.

Any help is appreciated.

EDIT: I've figured out that the problem is not with the subscripting, but rather that the string only has a length of 1 character.

2

There are 2 best solutions below

0
drake14w On BEST ANSWER

I've figured out what I was doing wrong. The picture for the variable FILE-DATA-HOLDER was limiting the number of characters that could be contained in the variable (obvious now that I think about it). All I had to do was change the picture of the aforementioned variable and slightly tweak the DISPLAY statement at the end.

Here is my updated code:

        IDENTIFICATION DIVISION.
        PROGRAM-ID. FILEREADER.

        ENVIRONMENT DIVISION.
        INPUT-OUTPUT SECTION.
        FILE-CONTROL.
            SELECT FILE-TO-READ
            ASSIGN USING INPUT-FILE-NAME 
            ORGANIZATION IS SEQUENTIAL
            ACCESS IS SEQUENTIAL.

        DATA DIVISION.
        FILE SECTION.
        FD FILE-TO-READ.
            01 CHAR PIC X(1).

        WORKING-STORAGE SECTION.
        01 FILE-DATA-POINTER USAGE IS POINTER.
        01 FILE-DATA-HOLDER PIC X(2147483646) BASED.
        01 INPUT-DATA.
            05 INPUT-FILE-NAME PIC X(100). 
            05 INPUT-FILE-CHARACTERS PIC 9(10).
        01 MISC-DATA.
           05 EOF PIC X(1).
           05 STRING-INDEX PIC 9(18) USAGE IS COMP. 
        PROCEDURE DIVISION.
        MAIN.
            PERFORM GET-INPUT.
            PERFORM ALLOCATE-STORAGE.
            PERFORM READ-FILE.
            PERFORM DISPLAY-FILE.
            PERFORM CLEAN-UP.
            STOP RUN.

        GET-INPUT.
            DISPLAY "Please enter the name of the file (maximum 100 char
      -     "acters)."
            ACCEPT INPUT-FILE-NAME.

            DISPLAY "Please enter the number of characters in your file
      -     "(the number of bytes)."
            ACCEPT INPUT-FILE-CHARACTERS.

        ALLOCATE-STORAGE.
            ALLOCATE INPUT-FILE-CHARACTERS CHARACTERS RETURNING
            FILE-DATA-POINTER SET ADDRESS OF FILE-DATA-HOLDER TO
            FILE-DATA-POINTER.
            
            IF ADDRESS OF FILE-DATA-POINTER = NULL
                DISPLAY "System error when allocating RAM. Exiting..."
                STOP RUN
            END-IF.

        READ-FILE.
            INITIALIZE STRING-INDEX.
            MOVE 'N' TO EOF.
            MOVE 1 TO STRING-INDEX.
            OPEN INPUT FILE-TO-READ.
                PERFORM UNTIL EOF = 'Y'
                    READ FILE-TO-READ 
                        AT END MOVE 'Y' TO EOF
                        NOT AT END MOVE CHAR TO FILE-DATA-HOLDER
                        (STRING-INDEX:1)

                        ADD 1 TO STRING-INDEX    
                    END-READ
                END-PERFORM.
           CLOSE FILE-TO-READ.
       
        DISPLAY-FILE.
            DISPLAY FILE-DATA-HOLDER (1:STRING-INDEX).

       
        CLEAN-UP.
            FREE FILE-DATA-POINTER.
1
Simon Sobisch On

I'm not 100% sure what your "single question" is, but there are some things that can be said:

I figured out that this is because strings can't be subscripted/indexed like in C or Python

They can referenced, it just is different:

  • COBOL calls that reference-modification, and always means "start:length" with that
  • the position is 1-based, not 0-based

-> Just as you've used it.
But the culprit possibly is that you enabled runtime checks - in this case you'd get an out-of-bound (which is also hinted by the compile warning you get).

The program outputs nothing

The first statement that is done (within the very first, a PERFORM) is a DISPLAY, followed by an ACCEPT - therefore if there is no output, then your GnuCOBOL installation or the way you execute that program seems to be broken.

you apparently can't dynamically allocate tables in COBOL

You can, COBOL2014 has that built-in as OCCURS DYNAMIC (but warning: GnuCOBOL doesn't support this in 2023). An extension from IBM (not available in Micro Focus COBOL but in GnuCOBOL) is OCCURS UNBOUNDED, which is explicit used for things like that. You'd define that as 01 FILE-DATA-HOLDER. 05 FILLER OCCURS UNBOUNDED DEPENDING ON INPUT-FILE-CHARACTERS PIC X. in LINKAGE SECTION.