Минимум знаний перед чтением исходного кода COBOL
· Обновлено: · Го Комура · COBOL, легаси, бизнес-системы, сопровождение, Mainframe
История изменений (1 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Первая публикация
Цитирование статьи(DOI: 10.5281/zenodo.21619734)
Статья заархивирована на Zenodo. Ниже приведены DOI, который всегда ведёт к последней версии, и DOI, закреплённый за версией, которую вы читаете.
Го Комура (2026). Минимум знаний перед чтением исходного кода COBOL. KomuraSoft LLC. https://doi.org/10.5281/zenodo.21619734 https://comcomponent.com/ru/blog/2026/03/17/001-cobol-minimum-reading-guide/
- DOI (последняя версия)
- 10.5281/zenodo.21619734
- DOI (эта версия)
- 10.5281/zenodo.21619735
Передача проекта, разбор сбоя, сопровождение пакета вендора. В таких ситуациях однажды на стол может упасть исходный код COBOL.
- Имена файлов —
.cblили.cpy - Имена переменных — сплошные заглавные
- Подряд идут
01,05,77,88 - Появляется запись вроде
PIC S9(7)V99 COMP-3— среднее между заклинанием и бухгалтерской программой - И ещё сплошной
COPY, так что по одному открытому файлу общей картины не видно
На этом месте большинство людей на время останавливается.
Но карта, по которой читать, не такая уж большая. У COBOL есть различия между компиляторами и продуктами, но скелет, который нужно взять первым при чтении существующей бизнес-системы, довольно общий. В этой статье, ориентируясь на линейку IBM и типичный бизнес-COBOL, разберём минимальный набор для тех, кому внезапно пришлось читать такой исходный код.
flowchart TB
accTitle: Почему стопорятся и насколько велика карта
accDescr: Непривычные имена заглавными, номера уровней, запись вроде PIC S9(7)V99 COMP-3 и обилие COPY, из-за которого не видно целого, заставляют остановиться. Но скелет для чтения существующей бизнес-системы довольно общий, и карта не такая уж большая.
iv1["Непривычная запись — и стопор"] --> iv2["Но скелет общий для разных компиляторов"]
iv2 --> iv3["Сначала держат только минимальную карту"]
iv1 -.-> iv4["Сплошной COPY, целого не видно"]
Рис. 1: То, что выглядит как заклинание, сводится к нескольким общим понятиям.
1. Сначала вывод (в одном предложении)
Скажем это сначала довольно грубо, но так, чтобы это пригодилось на практике.
- COBOL — прежде чем быть языком логики, довольно сильно язык описания записей
- Одного
PROCEDURE DIVISIONмало: вы увидите от силы половину. Сначала смотримDATA DIVISION PIC— это форма элемента,USAGE— в каком представлении он хранитсяCOMP-3— packed decimal. Часто встречается у сумм и количеств88— скорее не отдельная переменная, а condition-name: имя значения предыдущего элементаREDEFINES— другой взгляд на ту же память. Это не копия- Если есть
COPY, открытый файл ещё не полный. Без copybook общей картины не видно - Если умеете следить за
PERFORM,IF,EVALUATE,READ,WRITE,CALL, общий ход обработки уже улавливается - Старый исходник — это фиксированный формат, где позиция колонки имеет смысл. Видимые пробелы — не украшение1
Коротко: DIVISION, PIC, USAGE, COMP-3, REDEFINES, OCCURS, 88, COPY, PERFORM. Когда это читается, теряться начинают гораздо реже.
Карта знаний этой статьи
COBOL — это прежде всего язык, который в DATA DIVISION задаёт форму записи (record), и лишь затем язык процедур в PROCEDURE DIVISION; каждый элемент данных указывает форму в предложении PICTURE и внутреннее представление вроде COMP-3 в USAGE. COMP-3 — packed decimal, который упаковывает по две десятичные цифры в байт; если открыть его как текст, это выглядит как бессмысленная последовательность байтов. REDEFINES перечитывает ту же область в другой форме, OCCURS задаёт массив, уровень 88 — условие, которое даёт имя значению предыдущего элемента. Предложение COPY на этапе компиляции подключает внешний copybook, поэтому один открытый исходник не показывает полной картины; поток обработки нужно вести по PERFORM и scope terminator, а внешнюю границу фиксировать через FILE STATUS, EXEC SQL и EXEC CICS.
flowchart LR
accTitle: Карта знаний: чтение COBOL
accDescr: Схема показывает, что COBOL опирается на DATA DIVISION и PROCEDURE DIVISION; как элементы определения данных — PICTURE, USAGE, COMP-3, REDEFINES, OCCURS, уровень 88 и COPY — связаны с элементами управления вроде PERFORM и scope terminator и с внешними границами вроде EBCDIC, FILE STATUS, EXEC SQL и EXEC CICS.
cobol["COBOL"]
cobol_data_division["DATA DIVISION"]
comp_3["COMP-3 (packed decimal)"]
cobol_procedure_division["PROCEDURE DIVISION"]
cobol_picture_clause["фраза PICTURE (PIC)"]
cobol_usage_clause["фраза USAGE"]
comp_5["COMP-5"]
cobol_redefines["фраза REDEFINES"]
cobol_occurs["фраза OCCURS"]
cobol_occurs_depending_on["OCCURS DEPENDING ON"]
cobol_88_level["уровень 88 (имя условия)"]
cobol_copy["оператор COPY"]
copybook["copybook"]
cobol_perform["оператор PERFORM"]
cobol_scope_terminator["scope terminator"]
cobol_move["оператор MOVE"]
cobol_fixed_format["фиксированный формат (reference format)"]
ibm_enterprise_cobol["IBM Enterprise COBOL"]
ebcdic["EBCDIC"]
cobol_file_status["фраза FILE STATUS"]
exec_sql["EXEC SQL"]
exec_cics["EXEC CICS"]
cobol -->|"использует"| cobol_data_division
cobol -->|"использует"| cobol_procedure_division
cobol_data_division -->|"использует"| cobol_picture_clause
cobol_data_division -->|"использует"| cobol_usage_clause
cobol_usage_clause -->|"использует"| comp_3
cobol_usage_clause -->|"использует"| comp_5
cobol_data_division -->|"использует"| cobol_redefines
cobol_data_division -->|"использует"| cobol_occurs
cobol_occurs_depending_on -->|"использует"| cobol_occurs
cobol_data_division -->|"использует"| cobol_88_level
cobol -.->|"использует"| cobol_copy
cobol_copy -->|"использует"| copybook
cobol_procedure_division -->|"использует"| cobol_perform
cobol_procedure_division -->|"использует"| cobol_scope_terminator
cobol_procedure_division -->|"использует"| cobol_move
cobol -.->|"использует"| cobol_fixed_format
ibm_enterprise_cobol -->|"использует"| ebcdic
ibm_enterprise_cobol -.->|"использует"| cobol_fixed_format
cobol -.->|"использует"| cobol_file_status
cobol -.->|"использует"| exec_sql
cobol -.->|"использует"| exec_cics
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 21, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Считайте, что COBOL — прежде всего язык о форме данных
Если читать его рефлексами C# или Java, сначала хочется идти по if, for и вызовам функций.
В COBOL быстрее сначала понять, какие записи программа принимает, какие собирает и какими буферами располагает — и только потом идти дальше.
Типичный бизнес-COBOL идёт примерно так.
- Считать запись из файла или БД
- Положить её в элементы
WORKING-STORAGE - Разветвиться по условию
- Переложить данные в другую запись
- Записать результат
Иначе говоря, раскладка данных обычно встаёт раньше алгоритма.
flowchart TB
accTitle: Типичный поток бизнес-COBOL
accDescr: Считать запись из файла или БД, положить в элементы WORKING-STORAGE, разветвиться по условию, переложить в другую запись и записать результат — типичный поток бизнес-COBOL.
f1["Считать запись"] --> f2["Положить в WORKING-STORAGE"]
f2 --> f3["Разветвиться по условию"]
f3 --> f4["Переложить в другую запись"]
f4 --> f5["Записать результат"]
Рис. 2: Главное — поток записей; алгоритм встраивается между ними.
Например, такой скелет.
IDENTIFICATION DIVISION.
PROGRAM-ID. SAMPLE01.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT SALES-FILE ASSIGN TO ...
DATA DIVISION.
FILE SECTION.
FD SALES-FILE.
01 SALES-REC.
05 SALE-ID PIC 9(8).
05 SALE-AMOUNT PIC S9(7)V99 COMP-3.
WORKING-STORAGE SECTION.
01 WS-EOF PIC X VALUE 'N'.
88 EOF VALUE 'Y'.
PROCEDURE DIVISION.
PERFORM UNTIL EOF
READ SALES-FILE
AT END
SET EOF TO TRUE
NOT AT END
PERFORM PROCESS-SALE
END-READ
END-PERFORM
STOP RUN.
Читая этот код, первым делом смотрите не на PERFORM, а на тип SALE-AMOUNT и смысл EOF.
В таком порядке COBOL вдруг читается спокойно.
3. Сначала четыре DIVISION
Исходник COBOL прежде всего делится на четыре крупных DIVISION.
| DIVISION | Что смотреть в первую очередь |
|---|---|
IDENTIFICATION DIVISION |
Имя программы, старые комментарии, происхождение |
ENVIRONMENT DIVISION |
Файлы, внешние ресурсы, предпосылки ввода-вывода |
DATA DIVISION |
Определения записей, рабочие области, параметры |
PROCEDURE DIVISION |
Собственно порядок обработки |
Особенно важны следующие части.
FILE SECTIONздесь определения записей входных и выходных файловWORKING-STORAGE SECTIONповседневные переменные, флаги, счётчики, рабочие буферыLOCAL-STORAGE SECTIONиногда есть область, которая инициализируется при каждом вызовеLINKAGE SECTIONиногда есть параметры снаружи и точка приёма подпрограммы
Если видны LINKAGE SECTION и PROCEDURE DIVISION USING ..., велика вероятность, что программа не самодостаточна: она работает с данными, которые приходят снаружи.
flowchart TB
accTitle: На что указывает LINKAGE SECTION
accDescr: Если видны LINKAGE SECTION и PROCEDURE DIVISION USING, программа, скорее всего, не самодостаточна и работает с данными, которые приходят снаружи.
lk1["Есть LINKAGE SECTION"] --> lk3["Вероятно, данные приходят снаружи"]
lk2["Есть PROCEDURE DIVISION USING"] --> lk3
lk3 -.-> lk4["Это не самодостаточная программа"]
Рис. 3: Увидели определение приёма — читайте, исходя из того, что есть вызывающая сторона.
4. Не пугайтесь вида фиксированного формата
В старом COBOL сама позиция колонки в строке исходника несёт смысл. Без этого так и останется загадкой, «почему слева странный отступ».1
В фиксированном формате грубо так.
- Колонки 1–6: порядковый номер
- Колонка 7: indicator
- Колонки 8–11: Area A
- Колонки 12–72: Area B
Колонка 7 особенно важна.
*или/: строка комментария-: строка продолженияD: debugging line*>: комментарий, который можно поставить и в середине строки
Соответствие колонок и содержимого удобнее смотреть с линейкой. Первая строка — десятки, вторая — единицы.
1 2 3 4 5 6 7 8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
SSSSSSIAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB........
S= порядковый номер (колонки 1–6)I= indicator (колонка 7)A= Area A (колонки 8–11)B= Area B (колонки 12–72).= колонка 73 и дальше. Некоторые компиляторы используют это как поле идентификации, но на смысл программы оно не влияет
На реальном исходнике это выглядит так.
000100* эта строка — комментарий, потому что в колонке 7 стоит *
000200 IDENTIFICATION DIVISION.
000300 PROGRAM-ID. SAMPLE01.
000400 DATA DIVISION.
000500 WORKING-STORAGE SECTION.
000600 01 WS-ORDER.
000700 05 WS-ORDER-ID PIC 9(8).
000800 05 WS-LONG-NAME PIC X(30) VALUE 'ABCDEFGHIJKLMNOPQRST
000900- 'UVWXYZ0123'.
Четыре правила чтения.
- Колонки 1–6 — порядковый номер. В примере это
000100и подобные значения; на работу программы они не влияют. Бывает и пусто. - Колонка 7 — indicator.
*— комментарий,-— продолжение. В примере это строка000900: продолжение строкового литерала с предыдущей строки. - Колонки 8–11 — Area A. Отсюда начинают
DIVISION,SECTION, имена параграфов,FD, а также номера уровней01и77. В примере с Area A начинаютсяIDENTIFICATION DIVISION.и01 WS-ORDER. - Колонки 12–72 — Area B. Сюда пишут обычные операторы и младшие уровни вроде
05. В примере с Area B начинается05 WS-ORDER-ID.
Пробелы здесь — не «форматирование» в современном смысле, а отчасти синтаксис. Преобразовать табуляцию в редакторе, сдвинуть текст влево или небрежно скопировать — и код обычно ломается. Глядя на старый исходник, сначала спросите, fixed format это или free format. Современный автоформаттер на fixed-формате сдвигает границу Area A и Area B, и компиляция перестаёт проходить.
flowchart TB
accTitle: Прежде чем трогать фиксированный формат
accDescr: В старом исходнике сначала выясняют, fixed format это или free. Автоформатирование или замена табуляции на fixed-формате сдвигает границу Area A и Area B, и компиляция перестаёт проходить.
fx1["Открыли старый исходник"] --> fx2{"fixed или free?"}
fx2 -->|"fixed format"| fx3["Позиция колонки — часть синтаксиса"]
fx3 -.-> fx4["Автоформат и замена табуляции ломают код"]
fx2 -->|"free format"| fx5["Ограничения по колонкам слабее"]
Рис. 4: Сначала фиксируют формат файла, и только потом думают о «красоте».
5. Минимум по DATA DIVISION
5.1 Номера уровней
Иерархию данных в COBOL строят не отступами, а номерами уровней.2
01 WS-ORDER.
05 WS-ORDER-ID PIC 9(8).
05 WS-AMOUNT PIC S9(7)V99 COMP-3.
05 WS-STATUS PIC X.
88 WS-OK VALUE '0'.
88 WS-ERROR VALUE '9'.
77 WS-COUNT PIC 9(4).
Достаточно запомнить хотя бы это.
01: верхний уровень одной записи или группы02–49: уровни под ним77: независимый одиночный элемент88: condition-name. Даёт имя значению предыдущего элемента366: дляRENAMES. Встречается нечасто, но существует
Важно не считать 88 отдельной bool-переменной.
Отдельной области WS-OK нет: когда WS-STATUS равен '0', это же значение можно прочитать под именем WS-OK.
flowchart TB
accTitle: Что на самом деле такое уровень 88
accDescr: Уровень 88 — не отдельная bool-переменная, а condition-name: имя значения предыдущего элемента. WS-OK становится истинным, только когда WS-STATUS имеет нужное значение.
cn1["Базовый элемент WS-STATUS"] --> cn2{"какое значение?"}
cn2 -->|"когда 0"| cn3["Истинно под именем WS-OK"]
cn2 -->|"когда 9"| cn4["Истинно под именем WS-ERROR"]
cn3 -.-> cn5["Отдельной области нет"]
Рис. 5: 88 — не переменная, а удобное имя значения.
Ещё одно: иерархию задают номера уровней, а не пробелы.
Визуальный отступ полезен как подсказка, но в конечном счёте доверять нужно 01 / 05 / 10 / 88.2
5.2 PICTURE
PIC задаёт форму элемента.
Чаще всего встречаются такие обозначения.
| Запись | Примерный смысл |
|---|---|
X |
Символ |
9 |
Цифра |
S |
Со знаком |
V |
Десятичная точка только логическая |
X(10) |
10 символов |
9(5) |
5-разрядное число |
S9(7)V99 |
Со знаком, 7 разрядов целой части + 2 разряда дробной |
Например:
PIC X(10)→ 10 символовPIC 9(5)V99→ 5 разрядов целой части + 2 разряда дробнойPIC S9(7)V99→ со знаком, 7 разрядов целой части + 2 разряда дробной
Здесь особенно важен V.
V не хранит сам символ ..
PIC 9(5)V99 трактуется как «число с двумя десятичными разрядами», но точки в данных нет.
Поэтому если читать файл или дамп как «видимую строку символов», почти наверняка ошибётесь.
flowchart TB
accTitle: V — логическая десятичная точка
accDescr: V в PIC 9(5)V99 — логическая десятичная точка: символа точки в данных нет. Если читать файл или дамп как видимую строку, почти наверняка ошибётесь.
vd1["PIC 9(5)V99"] --> vd2["Трактуют как число с 2 десятичными разрядами"]
vd2 --> vd3["Символа точки в данных нет"]
vd3 -.-> vd4["Чтение как видимой строки ломает смысл"]
Рис. 6: Десятичная точка есть в определении, в данных её нет.
5.3 USAGE / DISPLAY / COMP / COMP-3
Если PIC — форма, то USAGE — в каком представлении элемент хранится.
Для чтения хватит следующего минимума.45
| Запись | Примерный смысл | На что смотреть при чтении |
|---|---|---|
DISPLAY |
Внешняя десятичная форма, видимая как символы | На mainframe нередко предполагается EBCDIC6 |
COMP / BINARY |
Двоичное число | Видимое число разрядов и внутреннее представление — разные вещи |
COMP-3 / PACKED-DECIMAL |
packed decimal | Как текст выглядит «битым» |
Например:
01 WS-AMOUNT-DISP PIC S9(7)V99.
01 WS-AMOUNT-BIN PIC S9(7) COMP.
01 WS-AMOUNT-PACK PIC S9(7)V99 COMP-3.
Все три — «числа», но способ хранения разный.
flowchart TB
accTitle: Одно и то же число, разное хранение
accDescr: Даже у числа со знаком USAGE меняет хранение: DISPLAY — внешняя десятичная форма, видимая как символы; COMP — двоичное число; COMP-3 — packed decimal, который как текст выглядит «битым».
us1["Числовой элемент той же формы"] --> us2["DISPLAY (видно как символы)"]
us1 --> us3["COMP (двоичное число)"]
us1 --> us4["COMP-3 (packed decimal)"]
us4 -.-> us5["Как текст выглядит «битым»"]
Рис. 7: Даже при том же PIC другой USAGE даёт другую последовательность байтов.
На практике сильнее всего выручает рефлекс в момент, когда видите COMP-3.
- Это packed decimal
- Скорее всего сумма, налог, количество или ставка
- Как текст выглядит «битым» — так и должно быть
- Смотреть в логике CSV или UTF-8 — верный способ ошибиться
С таким пониманием дампы и двоичные файлы гораздо реже вызывают лишнюю панику.
flowchart TB
accTitle: Рефлекс в момент, когда видите COMP-3
accDescr: Увидев COMP-3, понимают, что это packed decimal, скорее всего сумма, налог, количество или ставка, и что «битый» вид как текст ожидаем, поэтому дамп не смотрят в логике CSV или UTF-8.
cp1["Нашли COMP-3"] --> cp2["Поняли: packed decimal"]
cp2 --> cp3["Подозревают сумму или количество"]
cp3 --> cp4["«Битый» текст не пугает"]
Рис. 8: Одного этого рефлекса достаточно, чтобы реже паниковать перед дампом.
Какая последовательность байтов получается у COMP-3
Это стоит один раз пройти руками — дальше дампы читаются иначе.
У packed decimal всего два правила.45
- В один байт укладывают по две десятичные цифры
- Но в самом правом байте младшая цифра и знак занимают весь байт
Знак кодируется 4 битами: C — плюс, D — минус, F — без знака.
Проверим на примере из руководства IBM.4
| Определение | Значение | Байты |
|---|---|---|
PIC S9(4) PACKED-DECIMAL |
+1234 |
01 23 4C |
PIC S9(4) PACKED-DECIMAL |
-1234 |
01 23 4D |
PIC 9(4) PACKED-DECIMAL |
1234 |
01 23 4F |
У 1234 четыре разряда, поэтому из-за правила 2 в начале появляется свободный разряд. Это ведущий 0.
Тем же способом разберём PIC S9(7)V99 COMP-3, который в статье встречается не раз.
S9(7)V99— это 7 разрядов целой части + 2 разряда дробной = 9 разрядовVтолько отмечает позицию десятичной точки и не занимает ни одного байта- 9 разрядов по два на байт дают 4 байта, оставшийся разряд и знак — ещё 1 байт. Итого 5 байт
Если значение +12345.67, девять разрядов — это 001234567, и получается так.
значение : +12345.67
9 разрядов: 0 0 1 2 3 4 5 6 7 и знак
байты : 00 12 34 56 7C
^ знак C = плюс
Для отрицательного -12345.67 меняется только конец: 7D.
байты : 00 12 34 56 7D
Как это выглядит, если открыть как текст — вот в чём суть. Если насильно сопоставить 00 12 34 56 7C по одному символу ASCII на байт,
00— NUL,12— управляющий символ; как символы они не отображаются34—4,56—V,7C—|
На экране это похоже на «несколько непечатаемых символов, затем 4V|». Последовательности 12345.67 нигде нет.
В этом и есть «похоже на порчу кодировки, но данные целы». Открыли дамп, увидели бессмысленную последовательность — сначала спросите, нет ли у этого элемента USAGE COMP-3.
flowchart TB
accTitle: Как собираются байты COMP-3
accDescr: В packed decimal десятичные цифры укладывают по две в байт, а в последнем байте — младшая цифра и знак; 9-разрядное число занимает 5 байт, и при открытии как ASCII исходной последовательности цифр нигде нет.
pk1["По две десятичные цифры в байт"] --> pk2["Последний байт: младшая цифра и знак"]
pk2 --> pk3["9 разрядов — итого 5 байт"]
pk3 --> pk4["В ASCII исходного числа нет"]
pk4 -.-> pk5["Похоже на порчу кодировки, но данные целы"]
Рис. 9: Два правила упаковки превращают «непонятный дамп» в читаемую раскладку.
Заодно шпаргалка: из числа разрядов в число байтов. Байтов получается так: число девяток разделить на 2, дробную часть отбросить, прибавить 1.
| Число девяток в PICTURE | Байтов у COMP-3 |
|---|---|
| 1 | 1 |
| 2–3 | 2 |
| 4–5 | 3 |
| 6–7 | 4 |
| 8–9 | 5 |
| 10–11 | 6 |
| 12–13 | 7 |
Два разных числа разрядов дают одну длину, потому что свободный ведущий разряд появляется, только когда девяток чётное число. Поэтому S9(4) занимает 3 байта (01 23 4C), и S9(5) тоже укладывается в те же 3 байта.
При сверке layout с внешним файлом без этой таблицы смещение уезжает по одному байту.
Ещё одно уточнение: DISPLAY вовсе не обязательно означает строку ASCII.
В линейке z/OS по умолчанию предполагается EBCDIC, поэтому даже если цифры видны как символы, байтовые значения могут отличаться от ASCII '0'–'9'.6
5.4 REDEFINES / OCCURS / COPY / FILLER
Эти четыре конструкции — типичные места, где чтение стопорится.
REDEFINES
REDEFINES — другой взгляд на ту же область. Это не копия.7
01 REC-BUF.
05 REC-TYPE PIC X.
05 REC-DATA PIC X(99).
01 HEADER-REC REDEFINES REC-BUF.
05 HDR-TYPE PIC X.
05 HDR-DATE PIC 9(8).
05 FILLER PIC X(91).
По ощущению это близко к union в языках семейства C.
Часто пишут в духе «один и тот же блок из 100 байт различать как разные типы записи».
flowchart TB
accTitle: REDEFINES — другая интерпретация той же области
accDescr: REDEFINES — не копия, а другой взгляд на ту же область памяти: один буфер читают и как общую запись, и как заголовок; запись с одной стороны меняет вид с другой.
rd1["Одна область памяти"] --> rd2["Смотреть как REC-BUF"]
rd1 --> rd3["Смотреть как HEADER-REC"]
rd2 -.-> rd4["Запись с одной стороны меняет вид обеих"]
rd3 -.-> rd4
Рис. 10: Определений два, последовательность байтов — одна.
OCCURS
OCCURS — это массив. В COBOL его чаще называют table.
05 WS-ITEM OCCURS 12 TIMES.
10 WS-PRICE PIC 9(5).
Если дальше встречается OCCURS DEPENDING ON, это таблица переменной длины.
Тогда может сдвинуться даже позиция последующих элементов: если читать в логике фиксированной длины, легко промахнуться со смещением.8
flowchart TB
accTitle: На что смотреть в OCCURS DEPENDING ON
accDescr: OCCURS — массив; с DEPENDING ON это таблица переменной длины, и позиция последующих элементов может зависеть от значения. Смещения в логике фиксированной длины тогда неверны.
oc1["Нашли OCCURS"] --> oc2{"есть ли DEPENDING ON?"}
oc2 -->|"нет"| oc3["Таблица фиксированного числа элементов"]
oc2 -->|"есть"| oc4["Таблица переменной длины"]
oc4 -.-> oc5["Позиция последующих элементов тоже может сдвинуться"]
Рис. 11: Три слова DEPENDING ON меняют исходные допущения для расчёта смещений.
COPY
COPY — это include на этапе компиляции.
То есть открытый сейчас файл ещё не обязательно полный исходник.9
COPY CUSTOMER-REC.
COPY ERROR-MAP.
Совершенно обычно, когда определения записей, общие флаги, host-переменные для SQL и внешние интерфейсы загнаны в copybook.
Если из-за обилия COPY читать тяжело, быстрее проверить, можно ли посмотреть развёрнутый исходник или compiler listing. В IBM Enterprise COBOL есть опция MDECK: она выводит входной исходный код после обработки библиотек.10
flowchart TB
accTitle: Как читать исходник с COPY
accDescr: COPY — include на этапе компиляции, поэтому открытый файл может быть ещё неполным. Смотрят copybook; если читать тяжело, ищут развёрнутый исходник, compiler listing или вывод опции MDECK.
cy1["Нашли COPY"] --> cy2["Открытый файл может быть ещё неполным"]
cy2 --> cy3["Открыть copybook и проверить"]
cy2 -.->|"если читать тяжело"| cy4["Искать развёрнутый исходник или listing"]
Рис. 12: Число строк, которое вы видите сейчас, — ещё не вся программа.
FILLER
FILLER — элемент без имени.
Но «раз на него не ссылаются, значит он ни на что не влияет» — неверно.
Обычные роли:
- зарезервированная область
- дыра для совместимости со старой спецификацией
- выравнивание длины записи
- запас под
REDEFINES
У FILLER просто нет имени, но как число байтов он существует. Забудете — и при сопоставлении с внешним файлом картина поедет по одному байту.
flowchart TB
accTitle: Что будет, если не посчитать FILLER
accDescr: У FILLER нет имени, но байты он занимает: резерв или выравнивание длины записи. Если не учесть его в расчёте, сопоставление с внешним файлом смещается по одному байту.
fl1["FILLER — элемент без имени"] --> fl2["Как число байтов он существует"]
fl2 --> fl3{"учли в расчёте layout?"}
fl3 -->|"учли"| fl4["Совпадает с внешним файлом"]
fl3 -->|"забыли"| fl5["Смещение едет по одному байту"]
Рис. 13: У элемента, на который не ссылаются, тоже есть роль — длина.
6. Минимум по PROCEDURE DIVISION
Если DATA DIVISION — карта, то PROCEDURE DIVISION — маршрут.
6.1 PERFORM
PERFORM — базовый переход управления в COBOL.
Грубо говоря, вызвать обработку и вернуться.11
Чаще всего встречается такая форма.
PERFORM INIT-PROC
PERFORM UNTIL EOF
PERFORM READ-PROC
IF NOT EOF
PERFORM EDIT-PROC
PERFORM WRITE-PROC
END-IF
END-PERFORM
У PERFORM два основных вида.
- Out-of-line
PERFORM, который указывает на параграф или SECTION - Inline
PERFORM ... END-PERFORM, где блок пишут прямо на месте
В более старом коде обычно встречается и указание диапазона вроде PERFORM A-100 THRU A-199.
Это удобно, но если в середину добавить параграф, его легко случайно захватить, поэтому при чтении смотрите, где заканчивается диапазон.
flowchart TB
accTitle: Три формы PERFORM
accDescr: У PERFORM есть out-of-line, который вызывает параграф или SECTION и возвращается, inline с блоком на месте, и в старом коде диапазон через THRU, который легко захватит лишний параграф, если его вставить в середину.
pf1["Нашли PERFORM"] --> pf2["Out-of-line: вызов параграфа"]
pf1 --> pf3["Inline: блок на месте"]
pf1 --> pf4["Диапазон через THRU"]
pf4 -.-> pf5["Обязательно смотреть конец диапазона"]
Рис. 14: От формы PERFORM зависят и точка возврата, и диапазон, который нужно проследить.
6.2 IF / EVALUATE / область действия
Базовый инструмент условного ветвления — IF.
Про EVALUATE достаточно думать примерно как про switch/case.
На что смотреть — как заканчивается область действия (scope).12
Код с явными терминаторами вроде
END-IFEND-PERFORMEND-READ
читать ещё сравнительно легко.
Проблема — в старом коде. В COBOL точка . работает как неявный scope terminator и разом закрывает все ещё не закрытые операторы.12
То есть от одной точки зависит:
- докуда простирается
IF - докуда простирается
PERFORM - где начинается следующий sentence
К тому же NEXT SENTENCE — это не то же самое, что CONTINUE.
NEXT SENTENCE переходит за следующую точку, поэтому пункт назначения зависит от того, где стоит очередная ..12
При чтении старого COBOL правильный настрой — смотреть на точки, а не на концы строк.
flowchart TB
accTitle: Вес точки
accDescr: Код с явными терминаторами вроде END-IF читать легче, а в старом коде точка работает как неявный scope terminator и разом закрывает незакрытые операторы, так что одна позиция точки меняет диапазон IF и PERFORM.
sc1{"есть ли явный терминатор?"}
sc1 -->|"есть END-IF и подобные"| sc2["Диапазон читается легко"]
sc1 -->|"нет, старый код"| sc3["Точка — неявный терминатор"]
sc3 --> sc4["Одна позиция меняет диапазон"]
sc4 -.-> sc5["Смотреть на точки, не на концы строк"]
Рис. 15: Поток управления в старом коде держит позиция точки.
6.3 READ / WRITE / CALL
В бизнес-COBOL чаще всего встречаются эти.
READWRITEREWRITESTARTCALL
Особенно классический приём — READ ... AT END ....
READ IN-FILE
AT END
SET EOF TO TRUE
NOT AT END
PERFORM PROCESS-REC
END-READ
Если есть CALL 'SUBPGM' USING ..., управление уходит в другую программу.
Тогда LINKAGE SECTION и PROCEDURE DIVISION USING вызываемой стороны довольно ясно показывают форму передачи данных.
flowchart TB
accTitle: Как идти по CALL
accDescr: CALL уходит в другую программу; LINKAGE SECTION и PROCEDURE DIVISION USING вызываемой стороны показывают форму передачи аргументов.
ca1["Нашли CALL"] --> ca2["Уход в другую программу"]
ca2 --> ca3["Смотреть LINKAGE SECTION вызываемой стороны"]
ca3 --> ca4["Через USING понять форму передачи"]
Рис. 16: Смысл вызова читают вместе с точкой приёма на вызываемой стороне.
7. Что лежит снаружи COBOL
Мир COBOL довольно часто не замыкается на одном исходнике.
Снаружи остаются:
- определения файлов
- среда выполнения
- подключение к БД
- транзакционная среда
- управление заданиями (job)
Для чтения стоит освоить хотя бы следующее.
Файлы и FILE STATUS
FILE-CONTROL в ENVIRONMENT DIVISION и FILE SECTION / FD в DATA DIVISION читают парой.13
SELECT IN-FILE ASSIGN TO ...
FILE STATUS IS WS-FS.
FD IN-FILE.
01 IN-REC.
05 ...
Если есть FILE STATUS, туда попадает код результата после каждой операции ввода-вывода.
Читать файловые сбои или проверку EOF без этого не с чего начинать.14
flowchart TB
accTitle: Определение файла читают парой
accDescr: SELECT в FILE-CONTROL внутри ENVIRONMENT DIVISION и FD в DATA DIVISION читают парой. Если есть FILE STATUS, туда попадает код результата после каждого ввода-вывода — отсюда начинают чтение сбоев и EOF.
fs1["SELECT в FILE-CONTROL"] --> fs3["Вместе — одно определение файла"]
fs2["FD в FILE SECTION"] --> fs3
fs3 --> fs4["В FILE STATUS попадает код результата"]
fs4 -.-> fs5["Точка входа в чтение сбоев и EOF"]
Рис. 17: Образ файла записан в двух DIVISION.
EXEC SQL
Если это встречается, перед вами встроенный SQL.
EXEC SQL
SELECT ...
END-EXEC.
Тогда COBOL — «ёмкость для host-переменных», а фактические условия выборки и объекты обновления находятся на стороне SQL.
Поэтому кратчайший путь — читать содержимое EXEC SQL как обычный SQL.
EXEC CICS
Если это встречается, вы в транзакционном контексте CICS.15
EXEC CICS
RECEIVE MAP(...)
END-EXEC.
В этот момент это уже не просто чтение пакетной программы. Нужно читать вместе с внешним контекстом: экранами, транзакциями, кодами ответа, COMMAREA и так далее.
JCL и определения запуска
В mainframe batch не редкость, что какой dataset реально назначат и в каком порядке пойдут job’ы, лежит вне исходника COBOL. Если по одному исходному файлу не понять, «где вообще этот файл», обычно дело не в плохом коде, а в том, что вы смотрите ещё не на весь нужный контур.
flowchart TB
accTitle: Мир снаружи исходника
accDescr: Если есть EXEC SQL, фактические условия — на стороне SQL; если EXEC CICS — экраны и транзакции во внешнем контексте; в mainframe batch назначение dataset и поток job лежат в JCL. Мир COBOL разделён и за пределы исходника.
ex1["EXEC SQL"] --> ex7["Смотреть и снаружи исходника"]
ex3["EXEC CICS"] --> ex7
ex5["JCL и определения запуска"] --> ex7
ex7 -.-> ex2["Условия SQL, экранный контекст, поток job"]
Рис. 18: Непонятно не потому, что код плохой, а потому, что поле зрения ещё узкое.
Различия между компиляторами
Статья ориентирована на линейку IBM, но на практике встречаются и Micro Focus, и COBOL на Linux / Windows. Скелет общий, поэтому способ чтения не меняется, зато точки, где одна и та же запись даёт разный результат, известны заранее — их лучше знать, чтобы не ошибиться.
| Куда смотреть | z/OS (IBM Enterprise COBOL) | Открытые платформы (Micro Focus, COBOL для Linux / Windows и т. п.) |
|---|---|---|
| Кодировка | Предполагается EBCDIC6 | Предполагается ASCII6 |
| Reference format | Традиционный default — fixed; free тоже можно выбрать1 | Есть и fixed, и free; что является default, зависит от настроек сборки1 |
| Как ищут copybook | Указание библиотеки | Путь поиска в опциях компилятора |
| Диалект | — | Есть опция, которая переключает, под какую реализацию подстраиваться |
Особенно сильно бьёт кодировка. Даже у одного и того же PIC X(10) файл, записанный на z/OS и прочитанный как есть в Windows, даёт другие байтовые значения даже у цифр. Большая часть историй «перенесли — и всё исказилось» — про это, и это отдельно от COMP-3.
Ещё одно место, где легко расходятся внутренние представления чисел. В IBM Enterprise COBOL у BINARY / COMP-4 усечение идёт по числу разрядов, записанному в PICTURE, а у COMP-5 значение держится в пределах нативной ёмкости двоичного числа — 2 / 4 / 8 байт — и усечение тоже происходит по размеру этого двоичного поля.16 Поэтому PIC S9(4) COMP и PIC S9(4) COMP-5 выглядят одинаково, но верхняя граница допустимого значения разная. Если в коде, который обменивается значениями с другой системой именно как двоичные числа, встречается COMP-5, читайте это как сознательную запись.
Самый короткий путь сверить со своей средой такой.
- Сначала открыть определение сборки (makefile, JCL, настройки проекта). Какой компилятор и с какими опциями — это видно раньше, чем из исходника.
- Зафиксировать reference format (fixed / free). Ошибка здесь плюс автоформат в редакторе ломает код.
- Зафиксировать кодировку. EBCDIC или ASCII меняет чтение дампа.
- Пометить элементы семейства
COMP. Расхождения между реализациями почти всегда здесь.
flowchart TB
accTitle: Короткий путь сверить со своей средой
accDescr: Сначала открывают определение сборки и смотрят компилятор с опциями, фиксируют reference format (fixed или free), фиксируют кодировку EBCDIC или ASCII и помечают элементы семейства COMP, где расхождения между реализациями проявляются чаще всего.
ck1["Сначала открыть определение сборки"] --> ck2["Зафиксировать reference format"]
ck2 --> ck3["Зафиксировать кодировку"]
ck3 --> ck4["Пометить элементы семейства COMP"]
Рис. 19: Четыре шага до чтения исходника закрывают типичные аварии из-за различий компиляторов.
8. Минимальный безопасный порядок чтения
Когда внезапно приходится читать COBOL, безопасен такой порядок.
- Пройти все
COPYЕсли copybook можно открыть — открыть. Если нет — искать listing или развёрнутый исходник - Собрать определения записей уровня
01Составить список верхних уровней вFILE SECTION,WORKING-STORAGEиLINKAGE SECTION - Прочитать
PICиUSAGEОтличить суммы, даты, количества, коды, флаги - Найти
READ/WRITE/REWRITE/CALL/EXEC SQL/EXEC CICSСначала увидеть ввод-вывод и внешние границы - Пройти только первый основной путь
От начала
PROCEDURE DIVISIONпройти цепочкуPERFORM - Посмотреть
88и элементы статуса После этого легче читаются EOF, норма/ошибка, коды типа - Пометить
REDEFINES/OCCURS DEPENDING ON/COMP-3Они обязательно скажутся позже, поэтому заранее отметьте их как опасные места - Для файлов — посмотреть
FILE STATUSЭто заметно снижает ошибки чтения, связанные с I/O
При таком порядке не нужно сразу вчитываться во весь текст. С COBOL гораздо легче не пытаться понять всё на 100% с самого начала, а сначала закрепить три точки — записи, внешние границы, основной путь — и только потом идти в детали.
flowchart TB
accTitle: Безопасный порядок чтения
accDescr: Проходят все COPY и смотрят copybook, собирают определения записей уровня 01, читают форму через PIC и USAGE, поиском находят ввод-вывод и внешние границы, затем идут по цепочке PERFORM от начала PROCEDURE DIVISION и помечают опасные места.
ro1["Пройти все COPY"] --> ro2["Собрать уровень 01"]
ro2 --> ro3["Прочитать форму через PIC и USAGE"]
ro3 --> ro4["Найти ввод-вывод и внешние границы"]
ro4 --> ro5["Пройти цепочку PERFORM основного пути"]
ro5 -.-> ro6["Пометить опасные места"]
Рис. 20: Не сплошное чтение текста, а этот порядок — снаружи внутрь.
8.1 Упражнение: прочитайте это определение записи
Одного объяснения мало, поэтому ниже — небольшая запись. Сначала ответьте сами, потом смотрите решение.
01 CUST-REC.
05 CUST-ID PIC X(8).
05 CUST-NAME PIC X(20).
05 CUST-KBN PIC X.
88 CUST-NORMAL VALUE '0'.
88 CUST-VIP VALUE '1'.
05 CUST-BALANCE PIC S9(7)V99 COMP-3.
05 CUST-HIST OCCURS 3 TIMES.
10 HIST-DATE PIC 9(8).
10 HIST-AMOUNT PIC S9(5)V99 COMP-3.
05 FILLER PIC X(4).
Вопросы
- Сколько байтов занимает
CUST-RECцеликом? - С какого байта от начала записи начинается
CUST-BALANCE? - С какого байта от начала начинается
HIST-AMOUNTвторой строки? - Если в
CUST-KBNлежит'1', какое condition-name истинно? - Если открыть этот файл в текстовом редакторе, какие элементы выглядят «битыми»?
- Назовите две вещи, которых из этого определения не видно.
Ответы
-
74 байта. Разбор такой.
Элемент Расчёт Байт CUST-IDX(8)8 CUST-NAMEX(20)20 CUST-KBNX1 CUST-BALANCECOMP-3 на 9 разрядов 5 CUST-HIST(8 + 4)× 336 FILLERX(4)4 Итого 74 Уровень
88— condition-name, поэтому байтов не занимает. Посчитать его — частая ошибка. УFILLERнет имени, но 4 байта на месте. -
С 30-го байта. Перед ним
8 + 20 + 1 = 29байт, поэтому начинается со следующего. -
С 55-го байта.
CUST-HISTначинается с 35-го байта, одна строка — 12 байт, поэтому первая занимает байты 35–46, вторая — 47–58. Первые 8 байт строки —HIST-DATE, значитHIST-AMOUNTначинается с 55-го байта. -
CUST-VIP. Отдельной областиCUST-VIPнет: когдаCUST-KBNравен'1', то же значение можно прочитать под этим именем. -
CUST-BALANCEиHIST-AMOUNT. ОбаCOMP-3, поэтому как текст дают бессмысленную последовательность.HIST-DATE—PIC 9(8)вDISPLAY, и в ASCII-среде его можно прочитать как цифры вроде20260317. В среде EBCDIC цифры могут выглядеть как цифры, но байтовые значения будут другими, чем в ASCII. -
Например, такое.
- Пришло ли само это определение через
COPY. Без copybook не видно, та ли это версия, которая реально используется. - EBCDIC или ASCII у файла. От этого зависит, как выглядят
PIC XиPIC 9 DISPLAY. - Бывает ли в
CUST-KBNчто-то кроме'0'и'1'. Определены только два condition-name; это не гарантия, что других значений не придёт. - Атрибуты самого файла (длина записи, фиксированная она или нет,
FILE STATUS). Это видно только вENVIRONMENT DIVISIONиFD.
- Пришло ли само это определение через
Если ошиблись в вопросах 1 и 3, вернитесь к таблице разрядов и байтов в разделе 5.3. Ошибки чтения COBOL обычно начинаются отсюда.
9. Типичные места, где застревают
В конце — места, на которых новички спотыкаются с очень высокой вероятностью.
Считать REDEFINES «другой переменной»
Нет. Это другой взгляд на ту же область. Меняете одну сторону — меняется и то, как выглядит другая.7
Считать 88 «отдельным bool»
Нет.
Это просто имя значения предыдущего элемента. SET WS-OK TO TRUE на деле записывает соответствующее значение в базовый элемент.3
Игнорировать COPY и читать только тело файла
Открытый файл — ещё только половина целого. Совершенно обычно, когда определения полей, общие флаги и host-переменные целиком лежат снаружи.9
Считать MOVE простым присваиванием
MOVE — не просто memcpy.
В зависимости от типа приёмника могут выполняться преобразование, выравнивание разрядов, заполнение нулями, усечение, а также редактирование и обратное редактирование.17
Недооценивать влияние .
Точка . в COBOL тяжелее, чем кажется.
В старом коде без явных терминаторов ошибка в том, докуда эта точка закрывает, даёт неверное чтение потока управления.12
Считать packed decimal или EBCDIC «порчей кодировки»
Данные не обязательно «битые». Довольно часто это просто изначально не строка — или не ASCII.46
Считать, что после OCCURS DEPENDING ON позиции фиксированы
Элементы после таблицы переменной длины могут сдвигаться в зависимости от значения. Если читать в логике фиксированной длины, все расчёты смещений уедут.8
10. Шпаргалка: на что смотреть в первую очередь
| Найденное слово | О чём подумать в первую очередь |
|---|---|
01 |
Верхний уровень записи или группы. Отсюда брать общую картину |
88 |
Смысловое имя флага или кода состояния. Ключ к ветвлениям |
PIC X(...) |
Символьный элемент |
PIC 9(...) / S9(...)V... |
Числовой элемент. Проверить число разрядов и позицию десятичной точки |
COMP |
binary |
COMP-3 |
packed decimal. С высокой вероятностью сумма или количество |
REDEFINES |
Ту же область интерпретируют иначе |
OCCURS |
Массив / table |
OCCURS DEPENDING ON |
Переменная длина. Смотреть и на последующие позиции |
FILLER |
Имени нет, длина есть |
COPY |
Без copybook полного вида не видно |
PERFORM |
Скелет основного пути |
READ / WRITE / REWRITE |
Файловый I/O |
EXEC SQL |
Обработка БД |
EXEC CICS |
Транзакционная обработка |
FILE STATUS |
Код результата I/O |
11. Итог
COBOL сложен не потому, что он старый. Просто определения данных, внешние файлы и контекст выполнения тесно связаны, поэтому вход сначала плохо виден.
Ещё раз минимальный набор для чтения.
- Взять карту через
DIVISION - Сначала прочитать
DATA DIVISION - Прочитать форму элементов через
PICиUSAGE - Пометить
COMP-3,REDEFINES,OCCURS,88,COPY - Следить за
PERFORM,READ,WRITE,CALL - Закрепить внешние границы через
FILE STATUS,EXEC SQL,EXEC CICS - Не недооценивать, как работает
.
Когда это видно, COBOL перестаёт быть «загадочной древней магией» и становится «языком обработки записей». Легаси пугает не потому, что название старое, а потому что если с самого начала выбрать не тот масштаб, всё сразу становится непонятным. Если масштаб карты совпадает, читается на удивление обычно.
flowchart TB
accTitle: Читать в подходящем масштабе
accDescr: Берут карту через DIVISION, сначала читают форму в DATA DIVISION, идут по потоку через PERFORM и ввод-вывод, закрепляют внешние границы через FILE STATUS, EXEC SQL и EXEC CICS. В таком масштабе COBOL читается как обычный язык обработки записей.
sm1["Взять карту через DIVISION"] --> sm2["Прочитать форму в DATA DIVISION"]
sm2 --> sm3["Идти по потоку через PERFORM и ввод-вывод"]
sm3 --> sm4["Закрепить внешние границы"]
sm4 -.-> sm5["Древняя магия становится языком обработки записей"]
Рис. 21: Сложность — не в возрасте языка, а в масштабе, с которого начинают смотреть.
12. Источники
Основные источники, на которые ссылается статья. Надстрочные номера в тексте ведут сюда; стрелка в конце каждой записи возвращает к месту ссылки.
-
IBM, “Reference format” / IBM, “Area A or Area B” / Micro Focus, “Fixed Format” ↩ ↩2 ↩3 ↩4
-
IBM, “Level-numbers” ↩ ↩2
-
IBM, “Format 2: condition-name value” ↩ ↩2
-
IBM, “Examples: numeric data and internal representation” ↩ ↩2 ↩3 ↩4
-
IBM, “PACKED-DECIMAL (COMP-3)” ↩ ↩2
-
IBM, “The EBCDIC character set” / IBM, “Handling differences in ASCII SBCS and EBCDIC SBCS characters” ↩ ↩2 ↩3 ↩4 ↩5
-
IBM, “предложение REDEFINES” ↩ ↩2
-
IBM, “OCCURS DEPENDING ON clause” ↩ ↩2
-
IBM, “оператор COPY” ↩ ↩2
-
IBM, “PERFORM statement” / IBM, “Procedure division structure” ↩
-
IBM, “Scope terminators” / IBM, “Coding a choice of actions” ↩ ↩2 ↩3 ↩4
-
IBM, “FILE STATUS clause” / IBM, “Using file status keys” ↩
-
IBM, “Написание программ COBOL для выполнения под управлением CICS” ↩
-
IBM, “Computational items” ↩
-
IBM, “Elementary move rules” ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Внутреннее устройство виртуализации Windows (часть 3) — виртуальные машины, которые стартуют за секунды: почему WSL2, Windows Sandbox и контейнеры такие лёгкие
Почему WSL2 и Windows Sandbox стартуют за секунды и остаются лёгкими. Разбираем динамический базовый образ, direct map, динамическое расп...
Внутреннее устройство виртуализации Windows (часть 2) — память, которую не видит даже ядро: как устроены VBS, HVCI и Credential Guard
На совместимом оборудовании при чистой установке VBS включена по умолчанию: гипервизор и SLAT создают изоляцию сильнее ядра. Разбираем ус...
Внутреннее устройство виртуализации Windows (часть 1) — где работает ваш Windows: гипервизор и разделы
Если включить Hyper-V, хостовый Windows сам работает поверх гипервизора как корневой раздел. Разбираем основу виртуализации: роли VT-x, S...
Пул потоков Win32 — параллелизм через CreateThreadpoolWork без своих потоков
Не плодите ли вы CreateThread по всему нативному коду? Разбираем API пула потоков Win32, переработанный в Vista: четыре объекта work, tim...
Именованные каналы на практике — от проектирования до безопасности IPC в Windows
Практический разбор именованных каналов — стандартного IPC в Windows. По первоисточникам: выбор байтового режима и режима сообщений, серв...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Технические консультации и ревью дизайна
Как разбирать существующий COBOL, с чего начинать доработку, как увидеть внешние границы и как оценить картину до миграции — тема хорошо стыкуется с технической консультацией и ревью архитектуры.
Расследование ошибок и причин
Разбор сбоя сразу после передачи проекта и поиск, где в COBOL расходятся данные, удобно вести как расследование ошибки и анализ причины.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- С чего начинать чтение исходного кода COBOL?
- Одного PROCEDURE DIVISION мало: вы увидите от силы половину. COBOL — прежде всего язык описания записей и лишь потом язык логики, поэтому сначала открывают DATA DIVISION. Безопасный порядок такой: пройти все COPY и посмотреть copybook'и, собрать определения записей уровня 01, прочитать форму элементов через PIC и USAGE, найти READ, WRITE, CALL, EXEC SQL и EXEC CICS, чтобы увидеть ввод-вывод и внешние границы, и только потом пройти цепочку PERFORM от начала PROCEDURE DIVISION — один основной путь.
- Что означает PIC S9(7)V99 COMP-3?
- PIC задаёт форму элемента, USAGE — в каком представлении он хранится. S9(7)V99 — число со знаком: 7 разрядов целой части и 2 разряда дробной, но V — только логическая десятичная точка, символа точки в данных нет. COMP-3 — packed decimal; так часто описывают суммы, налоги, количества и ставки. Как текст это выглядит «битым», и так и должно быть: смотреть дамп в логике CSV или UTF-8 — верный способ ошибиться.
- Как понимать уровень 88 и REDEFINES?
- 88 — это не отдельная bool-переменная, а condition-name: имя, данное значению предыдущего элемента. SET WS-OK TO TRUE на деле записывает соответствующее значение в базовый элемент. REDEFINES — другой взгляд на ту же область памяти, не копия; по ощущению ближе к union в языках семейства C. Меняете одну сторону — меняется и то, как выглядит другая. Так часто различают типы записей в одном буфере.
- Что делать, если из-за обилия COPY не видно общей картины?
- COPY — это include на этапе компиляции, поэтому открытый файл ещё не обязательно полный исходник. Определения записей, общие флаги, host-переменные для SQL и внешние интерфейсы вполне обычно лежат в copybook. Если читать тяжело, быстрее проверить, есть ли развёрнутый исходник или compiler listing. В IBM Enterprise COBOL для этого есть опция MDECK: она выводит входной исходный код после обработки библиотек.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.