Как вы берете строку в 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
.