Как вы берете строку в COBOL из файла, когда позиция неизвестна?

Я новичок на сайте, а также COBOL. Я пытаюсь написать программу, которая читает 80-байтовый файл, находит определенную строку и захватывает другую строку, расположенную сразу после этого. Единственная проблема, с которой я столкнулся, заключается в том, что начальная позиция строки не всегда находится в одном и том же байте в файле. Например, строка, которую я пытаюсь найти ниже, это строка LENGTH(#####), которая встречается дважды по всему файлу:

ДЛИНА (14909135) ФАЙЛ (DD:EDIREC) MSGDATE(130723) MSGDATELONG(20130723)
MSGTIME(091053) MSGSEQO(001390) MSGNAME(00008557) MSGSEQNO(00001)
SESSIONKEY(XXXXXXXX) РАЗДЕЛЕННЫЙ (E) SYSNAME(XXXXX-XX) SYSLEVEL(XXXX) TIMEZONE(L)
DATATYPE(E) EDITYPE(XXX) SENDERFILE(#####) RECFM(????) RECLEN(#) RECDLM(E)
UNIQUEID(XXXXXXXX) SYSTYPE(##) SYSVER(#);
ПОЛУЧЕННАЯ УЧЕТНАЯ ЗАПИСЬ (XXXX) ПОЛЬЗОВАТЕЛЬСКИЙ (XXXXXXXX) КЛАСС (#E2) ЗАРЯД (3) ДЛИНА (14911043)
ФАЙЛИД (DD:EDIREC) MSGDATE(130723) MSGDATELONG(20130723) MSGTIME(093045)
MSGSEQO(001392) MSGSEQNO(00000) SESSIONKEY(XXXXXXXX) РАЗРЕШЕНО (C)
SYSNAME(XXXXX-XX) SYSLEVEL(XXXX) TIMEZONE(L) ДАННЫЙ ТИП (E) EDITYPE (НЕФОРМАЦИОННЫЙ)
SENDERFILE(XXXXXXXXXXXXX) RECFM(????) RECLEN(0) RECDLM(C) UNIQUEID(XXXXXXXX)
SYSTYPE(24) SYSVER(5);

Обратите внимание на две строки ДЛИНА (#####). Приведенный ниже код позволяет подсчитать количество раз, когда появляется строка длины, а также получить окончательный счетчик строки длины (что мне действительно нужно, числа в строке длины), но только тогда, когда они находятся в этих двух позициях:

    РАЗДЕЛ РАБОЧЕГО ХРАНЕНИЯ.

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

    01 WS-STRINGS.

       05 PIC X LENGTH STRING X(7) ЗНАЧЕНИЕ "ДЛИНА (").

    01 WS-COUNTERS.

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

    01 WS-CHAR-TOTALS.

       05 CHAR-TOTAL  PIC 9(11) ЗНАЧЕНИЕ НУЛЕВ.

       05 TMP-TOTAL  PIC X(11) ЗНАЧЕНИЕ НУЛЕВ.......

    ПРОЦЕДУРА ОТДЕЛА.

    2200-GET-MSG-TOTAL.

        ПРОВЕРЬТЕ WS-INPUT-RECORD
        TALLYING WS-MSG-COUNT для всей длины.

    2300-СИМ-TOTAL.

        ЕСЛИ WS-INPUT-RECORD(1:7) = ДЛИНА

           Переместите WS-INPUT-RECORD(8:9) в TMP-TOTAL

           UNSTRING TMP-TOTAL, ОТДЕЛЕННЫЙ '' '
           В ЧАР-ИТОГО

        END-IF

        ЕСЛИ WS-INPUT-RECORD(61:7) = ДЛИНА

           Переместите WS-INPUT-RECORD(68:9) в TMP-TOTAL

           UNSTRING TMP-TOTAL, ОТДЕЛЕННЫЙ '' '
               В ЧАР-ИТОГО

        END-IF

Код прекрасно работает для двух позиций, показанных в приведенном выше примере ввода. Но это не сработает, если LENGTH (####) окажется в любой другой позиции байта. Кроме кодирования 80 операторов IF для проверки каждого байта в файле на строку, есть ли более простой способ получить эти значения внутри парней длины? Я проверил много других постов, и я думал об использовании указателей или таблиц, но я не могу понять это.

3 ответа

Решение

Вы можете использовать цикл выполнения изменений для просмотра каждого блока строки в каждой строке, где каждый блок представляет собой строку длины строки, которую вы ищете. Вот пример, который работает в OpenCobol:

   IDENTIFICATION DIVISION.
   PROGRAM-ID. FIND-STRING.

   ENVIRONMENT DIVISION.
   INPUT-OUTPUT SECTION.
   FILE-CONTROL.
   SELECT IN-FILE ASSIGN TO 'SAMPLE-LEN.TXT'
       ORGANIZATION IS LINE SEQUENTIAL.

   DATA DIVISION.
   FILE SECTION.
   FD  IN-FILE.
   01  IN-RECORD                        PIC X(80).

   WORKING-STORAGE SECTION.
   01  END-OF-FILE-SWITCH               PIC XXX VALUE 'NO '.
       88  END-OF-FILE                  VALUE 'YES'.
   01  STRING-MARKER                    PIC X(7) VALUE 'LENGTH('.
   01  STRING-MARKER-LENGTH             PIC 99 VALUE 7.
   01  STRING-SOUGHT                    PIC X(11).
   01  STRING-INDEX                     PIC 99.
   01  RECORD-LENGTH                    PIC 99 VALUE 80.

   PROCEDURE DIVISION.
   MAIN.
       OPEN INPUT IN-FILE
       PERFORM UNTIL END-OF-FILE
           READ IN-FILE
               AT END
                   SET END-OF-FILE TO TRUE
               NOT AT END
                   PERFORM FIND-STRING
           END-READ
       END-PERFORM
       CLOSE IN-FILE
       STOP RUN
       .

   FIND-STRING.
       PERFORM VARYING STRING-INDEX FROM 1 BY 1
           UNTIL STRING-INDEX > (RECORD-LENGTH
                                 - STRING-MARKER-LENGTH)
           IF IN-RECORD(STRING-INDEX:STRING-MARKER-LENGTH) =
              STRING-MARKER
              UNSTRING IN-RECORD(STRING-INDEX
                                 + STRING-MARKER-LENGTH : 10)
                  DELIMITED BY ')' INTO STRING-SOUGHT 
              END-UNSTRING 
              DISPLAY STRING-SOUGHT END-DISPLAY 
           END-IF 
       END-PERFORM 
       . 

Используйте INSPECT, чтобы установить, что ДЛИНА (находится в текущей записи.

Только при наличии сделайте следующее:

UNSTRING с использованием длины (в качестве разделителя с двумя полями получения.

НЕПРЕРЫВНОЕ второе поле получения, отделенное от) оставив вас с номером.

Например:

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

Отказаться от места назначения-1. Используйте destination-field-2 для ввода второго UNSTRING.

Используйте осмысленные имена, а не те, которые я показал, чтобы осветить пример.

Так,

    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 
               "<" 
       . 

Метод с INSPECT, чтобы увидеть, присутствует ли "строка", может быть применен к другому принятому решению, так что только если строка содержит желаемое значение, он "ищется".

Основываясь на комментариях Билла Вуджера, вот лучшее решение. Благодарю Билла, за то, что он научил меня не сутулиться:) Мне все еще нравится проходить по каждой записи как способ поймать несколько совпадений в одной строке, поэтому я сохранил эту часть.

   IDENTIFICATION DIVISION.
   PROGRAM-ID. FIND-STRING-2.

   ENVIRONMENT DIVISION.
   INPUT-OUTPUT SECTION.
   FILE-CONTROL.
   SELECT IN-FILE ASSIGN TO 'SAMPLE-LEN.TXT'
       ORGANIZATION IS LINE SEQUENTIAL
       FILE STATUS IS IN-FILE-STATUS.

   DATA DIVISION.
   FILE SECTION.
   FD  IN-FILE.
   01  IN-RECORD                        PIC X(80).

   WORKING-STORAGE SECTION.
   01  IN-FILE-STATUS                   PIC XX.
   01  END-OF-FILE-SWITCH               PIC XXX VALUE 'NO '.
       88  END-OF-FILE                  VALUE 'YES'.
   01  STRING-MARKER-LEFT               PIC X(7) VALUE 'LENGTH('.
   01  STRING-MARKER-RIGHT              PIC X VALUE ')'.
   01  STRING-MARKER-LENGTH             PIC 99 USAGE BINARY.
   01  STRING-INDEX                     PIC 99 USAGE BINARY.
   01  START-INDEX                      PIC 99 USAGE BINARY.
   01  END-INDEX                        PIC 99 USAGE BINARY.
   01  RECORD-LENGTH                    PIC 99 USAGE BINARY.
   01  SEARCH-LENGTH                    PIC 99 USAGE BINARY.
   01  IS-END-FOUND                     PIC XXX VALUE 'NO '.
       88  END-FOUND                    VALUE 'YES'.
       88  END-NOT-FOUND                VALUE 'NO '.

   PROCEDURE DIVISION.
   MAIN.
       OPEN INPUT IN-FILE

       IF IN-FILE-STATUS NOT = '00'
           DISPLAY 'FILE READ ERROR ' IN-FILE-STATUS
           END-DISPLAY
           PERFORM EXIT-PROGRAM
       END-IF

       PERFORM INITIALIZE-LENGTHS

       PERFORM UNTIL END-OF-FILE
           READ IN-FILE
               AT END 
                   SET END-OF-FILE TO TRUE
               NOT AT END
                   PERFORM FIND-STRING
           END-READ
       END-PERFORM
       PERFORM EXIT-PROGRAM
       .

   INITIALIZE-LENGTHS.
       MOVE FUNCTION LENGTH(IN-RECORD) TO RECORD-LENGTH 
       COMPUTE STRING-MARKER-LENGTH = FUNCTION LENGTH(
           STRING-MARKER-LEFT)
       END-COMPUTE
       COMPUTE SEARCH-LENGTH = RECORD-LENGTH - STRING-MARKER-LENGTH
       END-COMPUTE
       .

   FIND-STRING.
       PERFORM VARYING STRING-INDEX FROM 1 BY 1
           UNTIL STRING-INDEX > SEARCH-LENGTH 
           IF IN-RECORD(STRING-INDEX:STRING-MARKER-LENGTH) =
              STRING-MARKER-LEFT
              COMPUTE START-INDEX = STRING-INDEX 
                  + STRING-MARKER-LENGTH
              END-COMPUTE
              SET END-NOT-FOUND TO TRUE
              PERFORM VARYING END-INDEX FROM START-INDEX BY 1 
              UNTIL END-INDEX > RECORD-LENGTH OR END-FOUND
                  IF IN-RECORD(END-INDEX:
                  FUNCTION LENGTH(STRING-MARKER-RIGHT)) = 
                  STRING-MARKER-RIGHT
                      SET END-FOUND TO TRUE
                  END-IF
              END-PERFORM
              COMPUTE END-INDEX = END-INDEX - START-INDEX - 1
              END-COMPUTE
              DISPLAY IN-RECORD(START-INDEX:END-INDEX)
              END-DISPLAY
           END-IF
       END-PERFORM
       .

   EXIT-PROGRAM.
       CLOSE IN-FILE
       STOP RUN
       .

Другие вопросы по тегам