Уровни записей: 01, 05, 88

Разбираемся, как из простых PIC-полей строится иерархическая структура — «карточка» с вложенными данными — и что за особый уровень 88 умеет заменять сравнение на понятное имя.

Уровень записи — число в начале объявления COBOL-переменной, которое показывает её место в иерархии: является ли она самостоятельной записью или частью более крупной структуры.

Зачем данным иерархия

До сих пор мы объявляли переменные по одной: имя, возраст, зарплата — каждая сама по себе. Но в реальной программе часто нужно сгруппировать связанные данные вместе — например, «адрес» логично состоит из города, улицы и дома, а «сотрудник» — из имени, должности и зарплаты. В Python для этого используют словари или классы: sotrudnik = {"imya": "Ivanov", "dolzhnost": "Buhgalter"}. В COBOL для той же цели служат уровни записей — числа вроде 01, 05, 10, которые показывают, что одно поле «вложено» в другое, подобно папкам и файлам на компьютере.

Уровень 01 всегда обозначает вершину структуры — самостоятельную запись целиком. Всё, что вложено внутрь неё, получает более крупные числа уровня — обычно с шагом 05: 05, 10, 15 и так далее. Чем больше число уровня, тем глубже вложенность, примерно как заголовки h2 и h3 на веб-странице показывают структуру статьи.

Пример: карточка сотрудника

DATA DIVISION.
WORKING-STORAGE SECTION.
01 SOTRUDNIK.
    05 IMYA        PIC X(20) VALUE "Ivanov".
    05 DOLZHNOST    PIC X(15) VALUE "Buhgalter".
    05 ZARPLATA     PIC 9(6) VALUE 45000.

PROCEDURE DIVISION.
    DISPLAY "Zapis celikom: " SOTRUDNIK.
    DISPLAY "Tolko imya: " IMYA.
    STOP RUN.

Вывод:

Zapis celikom: Ivanov              Buhgalter      045000
Tolko imya: Ivanov

Разбираем построчно

Строка 01 SOTRUDNIK. объявляет запись верхнего уровня — обратите внимание, у неё нет PIC-клаузы, потому что сама по себе она не хранит данные напрямую, а просто «называет» группу. Дальше идут три поля уровня 05: IMYA, DOLZHNOST и ZARPLATA — каждое со своей PIC-клаузой, как мы разбирали в предыдущем уроке. Эти три поля физически и составляют содержимое записи SOTRUDNIK — если сложить их длины (20 + 15 + 6 = 41 байт), это и будет полный размер записи целиком.

В программе можно обратиться и к записи целиком по имени SOTRUDNIK — тогда COBOL напечатает все вложенные поля подряд, как единую строку — и к отдельному вложенному полю по имени IMYA, если нужно только оно. Это удобно: иногда нужно обработать запись как единый блок (например, записать в файл), а иногда — работать только с одним конкретным полем внутри неё.

Уровень 88 — не поле, а именованное условие

Особый и очень практичный случай — уровень 88. Он не описывает отдельные данные, а даёт имя условию, связанному со значением поля уровнем выше. Представьте, что у вас есть поле «статус заказа», которое может быть 1 (новый), 2 (в обработке) или 3 (завершён). Вместо того чтобы в каждом месте кода писать громоздкое сравнение IF STATUS = 3, можно один раз объявить понятное имя для этого условия.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 STATUS-ZAKAZA PIC 9 VALUE 1.
    88 ZAKAZ-NOVYY      VALUE 1.
    88 ZAKAZ-V-RABOTE   VALUE 2.
    88 ZAKAZ-ZAVERSHEN  VALUE 3.

PROCEDURE DIVISION.
    IF ZAKAZ-NOVYY
        DISPLAY "Zakaz tolko sozdan"
    END-IF.
    STOP RUN.

Вывод:

Zakaz tolko sozdan

Разбираем условное имя подробно

Строка 01 STATUS-ZAKAZA PIC 9 VALUE 1. — это обычное числовое поле из одной цифры с начальным значением 1. А дальше идут три строки уровня 88: каждая не создаёт новую переменную, а просто говорит «условие ZAKAZ-NOVYY истинно тогда, когда STATUS-ZAKAZA равен 1». Дальше в PROCEDURE DIVISION вместо явного сравнения IF STATUS-ZAKAZA = 1 мы пишем просто IF ZAKAZ-NOVYY — код становится читаемым почти как обычное предложение на английском, что и было исторической целью языка COBOL (COmmon Business-Oriented Language дословно и означает «общий бизнес-ориентированный язык» — его специально придумывали так, чтобы менеджеры, не программисты, могли хотя бы читать код).

Это отдалённо похоже на именованные константы или enum в современных языках (например, OrderStatus.NEW в Java), но работает даже более прямолинейно — уровень 88 буквально привязывается к конкретному значению конкретного поля, а не создаёт отдельный самостоятельный тип.

Мини-пример: карточка ученика с условием

Соберём три идеи раздела вместе: группировку через уровни 01/05 и условное имя через уровень 88.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 UCHENIK-ZAPIS.
    05 IMYA-UCH   PIC X(15) VALUE "Sidorova".
    05 OTSENKA    PIC 9 VALUE 5.
        88 OTLICHNO VALUE 5.

PROCEDURE DIVISION.
    IF OTLICHNO
        DISPLAY IMYA-UCH " uchitsya na otlichno"
    END-IF.
    STOP RUN.

Вывод:

Sidorova uchitsya na otlichno

Здесь UCHENIK-ZAPIS уровня 01 — вся запись целиком, IMYA-UCH и OTSENKA уровня 05 — два вложенных поля, а OTLICHNO уровня 88 — условное имя, привязанное к конкретному значению оценки. Заметьте, что уровень 88 в этом примере вложен ещё глубже — он идёт сразу после поля OTSENKA, к которому относится, а не после всей записи.

Частые ошибки новичков

Первая ошибка — пытаться дать записи уровня 01 собственную PIC-клаузу, если у неё есть вложенные поля. Запись-группа (та, что содержит другие поля) PIC не имеет — её «форма» целиком определяется суммой вложенных полей.

Вторая ошибка — перепутать порядок вложенности уровней. Числа уровней не обязаны идти строго по единице, но должны логично возрастать сверху вниз: нельзя, чтобы поле уровня 05 шло раньше уровня 01, или чтобы поле с меньшим числом уровня оказалось «внутри» поля с большим — иерархия должна быть последовательной, как вложенные списки.

Третья ошибка — забыть, что уровень 88 не создаёт переменную, которую можно менять напрямую. Нельзя написать MOVE 5 TO ZAKAZ-NOVYY — вместо этого значение меняют у поля-родителя (STATUS-ZAKAZA), а условие 88 лишь проверяет результат. Начинающие иногда путают условное имя с обычным полем и удивляются ошибке компиляции.

Итоги

  • Уровни записей (числа вроде 01, 05, 10) строят иерархию данных: запись верхнего уровня 01 объединяет вложенные поля с бо́льшими числами уровня.
  • Запись-группа без вложенных PIC-полей не хранит данные напрямую — её размер определяется суммой всех вложенных полей.
  • Уровень 88 — особый случай: он не поле, а именованное условие, привязанное к конкретному значению поля-родителя, что делает код читаемым почти как обычный текст.
  • Такая структура данных — предшественник современных вложенных объектов и enum-типов, только оформленная под язык, придуманный для мейнфреймов 1960-х годов.
Проверьте себя
1. Что означает уровень 01 в объявлении переменной COBOL, если после него следуют поля уровня 05?
AУровень 01 задаёт приоритет выполнения операции
BУровень 01 — вершина иерархии, запись-группа, объединяющая вложенные поля уровня 05
CУровень 01 означает, что переменная используется только один раз
DУровень 01 — это номер строки в файле программы
2. Что происходит при объявлении уровня 88, например 88 ZAKAZ-NOVYY VALUE 1?
AСоздаётся новая переменная ZAKAZ-NOVYY, в которую можно записывать любые числа
BСоздаётся именованное условие: ZAKAZ-NOVYY истинно, когда поле-родитель равно значению 1, а не самостоятельное поле данных
CУровень 88 задаёт максимальный размер поля в байтах
DУровень 88 используется только для объявления файлов