5.16. Типы данных
Типы данных
COBOL — один из самых ранних языков программирования высокого уровня, разработанный в конце 1950-х годов с целью упрощения обработки бизнес-данных. В отличие от многих современных языков, где типизация часто строится на абстрактных конструкциях и объектах, COBOL опирается на четкую, декларативную модель описания данных, ориентированную на их внешний вид и поведение в контексте документооборота, бухгалтерии, логистики и других прикладных областей. Основой этой модели служит понятие типа данных, реализованного через механизм PICTURE (PIC) и структуру групповых записей.
PICTURE (PIC) — описание формата данных
В COBOL тип данных не задается ключевым словом вроде int или string. Вместо этого используется спецификация PICTURE, сокращённо PIC, которая описывает формат поля: сколько символов оно содержит, какие символы допустимы, присутствует ли знак, десятичная точка, валютный символ и так далее. Эта спецификация позволяет точно определить, как данные будут храниться, отображаться и обрабатываться.
Каждое поле данных в COBOL объявляется в разделе DATA DIVISION, внутри секции WORKING-STORAGE SECTION или FILE SECTION, и сопровождается PIC-предложением. PIC-строка состоит из специальных символов, каждый из которых имеет строго определённое значение:
- 9 — цифра от 0 до 9. Используется для числовых данных.
- X — любой печатаемый символ (буква, цифра, знак препинания, пробел).
- A — только алфавитный символ (буква).
- S — указывает наличие знака «плюс» или «минус» перед числом.
- V — неявная десятичная точка. Она не занимает места в памяти, но указывает, где находится дробная часть.
- P — используется для масштабирования числа, сдвигая десятичную точку влево или вправо без изменения количества цифр.
- Z — используется в редактируемых числовых полях для подавления незначащих нулей слева.
- * — заменяет незначащие нули звёздочками (часто для защиты конфиденциальных данных).
- B — вставляет пробел вместо нуля.
- , и . — используются в редактируемых полях для явного отображения разделителей тысяч и десятичной точки.
- $ — символ валюты, отображается в начале числа.
Эти символы могут комбинироваться и повторяться с помощью круглых скобок. Например, 9(5) эквивалентно 99999, а X(10) — это десять произвольных символов.
Числовые типы данных
Числовые данные в COBOL делятся на несколько категорий в зависимости от представления и использования.
-
Целые числа:
Пример:PIC 9(5)— целое число, занимающее пять позиций. Максимальное значение — 99999. Такое поле может использоваться для хранения, например, количества единиц товара на складе. -
Числа со знаком:
Пример:PIC S9(4)— четырёхзначное целое число со знаком. Знак может быть отдельным символом или закодирован в последней цифре (в зависимости от компилятора и параметров). Такие поля подходят для балансов, температур, финансовых показателей, которые могут быть отрицательными. -
Десятичные числа с фиксированной точкой:
Пример:PIC 9(3)V99— число из трёх целых цифр и двух дробных. Фактически хранится как пять цифр, но программа интерпретирует его как, например, 123.45. Это стандартный способ хранения денежных сумм, весов, процентов и других величин, требующих точности. -
Масштабированные числа:
Пример:PIC PPP999— число, умноженное на 10⁻³. Это означает, что значение 123 будет интерпретировано как 0.123. Такой подход позволяет работать с очень малыми числами без использования явной десятичной точки. -
Редактируемые числовые поля:
Пример:PIC $$$,$$9.99— формат для отображения суммы с разделителями тысяч и двумя знаками после запятой. При выводе значение 123456 будет представлено как$1,234.56. Эти поля предназначены исключительно для отображения, а не для вычислений.
Символьные типы данных
Символьные данные в COBOL описываются с помощью символов X и A.
-
Алфавитно-цифровые строки:
Пример:PIC X(20)— строка длиной 20 символов, содержащая любые печатаемые символы. Это самый распространённый тип для хранения имён, адресов, описаний, кодов и другой текстовой информации. -
Алфавитные строки:
Пример:PIC A(15)— строка из 15 букв. Такие поля гарантируют, что в них не попадут цифры или знаки препинания. Они применяются, когда требуется строгая проверка содержимого, например, для фамилий в некоторых системах.
Символьные поля всегда имеют фиксированную длину. Если строка короче объявленной длины, она дополняется пробелами справа. Это свойство важно учитывать при сравнении и обработке.
Редактируемые и не редактируемые поля
COBOL различает не редактируемые (computational) и редактируемые (edited) поля. Первые предназначены для хранения и вычислений, вторые — для вывода. Переход между ними осуществляется с помощью операторов MOVE и COMPUTE, где значения автоматически преобразуются в соответствии с PIC-спецификацией.
Например, внутреннее поле AMOUNT PIC 9(5)V99 может содержать значение 1234567, которое интерпретируется как 12345.67. При копировании в редактируемое поле DISPLAY-AMOUNT PIC ZZZ,ZZ9.99, результат будет отображён как 12,345.67.
Групповые записи (GROUP ITEM)
COBOL поддерживает иерархическую структуру данных. Поля могут объединяться в групповые записи — составные элементы, которые сами по себе не содержат данных, но служат контейнерами для других полей. Это позволяет моделировать сложные бизнес-объекты, такие как клиент, заказ, счет или транзакция.
Групповая запись объявляется с уровнем (level number), обычно начинающимся с 01. Вложенные поля получают более высокие номера уровней, например 05 или 10. Уровень 01 обозначает корневой элемент записи. Уровни 66 и 88 имеют специальное назначение (для переименования и условных значений соответственно) и не используются для группировки.
Пример:
01 CUSTOMER-RECORD.
05 CUSTOMER-ID PIC 9(6).
05 CUSTOMER-NAME.
10 FIRST-NAME PIC X(20).
10 LAST-NAME PIC X(25).
05 ADDRESS PIC X(50).
05 BALANCE PIC S9(7)V99.
В этом примере CUSTOMER-RECORD — групповая запись. Она включает простые поля (CUSTOMER-ID, ADDRESS, BALANCE) и вложенную группу CUSTOMER-NAME, которая, в свою очередь, содержит FIRST-NAME и LAST-NAME.
Групповые записи позволяют:
- Обращаться к целому блоку данных одним именем. Например, можно скопировать всю запись
CUSTOMER-RECORDв другую переменную того же формата. - Читать и записывать структурированные данные из файлов, где каждая строка соответствует одной групповой записи.
- Упрощать логику программы за счёт логической группировки связанных полей.
При работе с файлами, особенно в мэйнфреймовых системах, групповые записи часто соответствуют фиксированным строкам входных или выходных данных. Это делает COBOL особенно эффективным для обработки больших массивов однородных записей — например, банковских транзакций или страховых полисов.
Особенности типизации в COBOL
Типизация в COBOL является статической и строгой. Все данные должны быть объявлены заранее, и их формат фиксирован на этапе компиляции. Это обеспечивает предсказуемость и надёжность, особенно в критически важных системах, где ошибки могут привести к финансовым потерям.
Однако COBOL допускает неявные преобразования при операциях MOVE и COMPUTE, если форматы совместимы. Например, можно переместить значение из PIC 9(3) в PIC 9(5), и оно будет дополнено нулями слева. Но попытка переместить символьную строку в числовое поле вызовет ошибку времени выполнения или некорректный результат, в зависимости от настроек компилятора.
Для повышения безопасности и читаемости рекомендуется использовать явные преобразования и проверки, особенно при работе с внешними данными.
Уровни 66 и 88 — специальные конструкции для работы с данными
В дополнение к стандартным уровням (01, 05, 10 и так далее), COBOL предоставляет два особых уровня: 66 и 88. Они не описывают физические данные, но играют важную роль в логике программы и управлении значениями.
Уровень 66 — переименование (RENAMES)
Уровень 66 позволяет создать альтернативное имя для части или целого блока данных. Это особенно полезно, когда один и тот же участок памяти должен интерпретироваться по-разному в зависимости от контекста.
Пример:
01 TRANSACTION-RECORD.
05 TX-DATE PIC X(8).
05 TX-AMOUNT PIC 9(7)V99.
05 TX-STATUS PIC X(1).
66 TX-HEADER RENAMES TX-DATE THRU TX-AMOUNT.
Здесь TX-HEADER — это псевдоним, объединяющий поля TX-DATE и TX-AMOUNT. При необходимости можно скопировать или обработать оба поля как единый блок, не перечисляя их по отдельности. Переименование не создаёт новой памяти — оно лишь предоставляет другое представление существующих данных.
Такой подход упрощает работу с переменными частями записей, особенно в устаревших системах, где форматы данных могут меняться в зависимости от типа транзакции.
Уровень 88 — условные имена (Condition Names)
Уровень 88 определяет логические условия, связанные с конкретным значением поля. Это мощный инструмент для повышения читаемости кода и избежания «магических значений».
Пример:
01 ACCOUNT-STATUS PIC X.
88 ACTIVE VALUE 'A'.
88 CLOSED VALUE 'C'.
88 SUSPENDED VALUE 'S'.
В этом случае поле ACCOUNT-STATUS может принимать значения 'A', 'C' или 'S'. Вместо того чтобы писать:
IF ACCOUNT-STATUS = 'A' THEN ...
можно использовать:
IF ACTIVE THEN ...
Это делает программу более понятной, особенно для аналитиков и бизнес-пользователей, которые могут читать COBOL-код без глубоких технических знаний. Уровень 88 также поддерживает множественные значения:
88 VALID-CODES VALUE '01', '02', '03', '99'.
и диапазоны:
88 HIGH-BALANCE VALUE 10000 THRU 999999.
Такие конструкции позволяют выразить бизнес-правила прямо в структуре данных, а не только в логике выполнения.
Редактируемые поля и форматированный вывод
Редактируемые поля в COBOL — это особый класс данных, предназначенный исключительно для отображения информации в человекочитаемом виде. Они не подходят для арифметических операций, но идеальны для генерации отчётов, счетов, накладных и других документов.
Основные символы редактирования:
- Z — подавляет незначащие нули слева, заменяя их пробелами.
- * — заменяет незначащие нули звёздочками (часто используется в финансовых отчётах для защиты конфиденциальности).
- B — вставляет пробел вместо нуля.
- $ — отображает символ валюты.
- , — разделяет тысячи.
- . — десятичная точка.
- CR/DB — указывает кредит или дебет (в некоторых реализациях).
Пример:
01 SALARY-DISPLAY PIC $$$,$$9.99.
01 SECRET-AMOUNT PIC ***,**9.99.
Если внутреннее значение равно 1234567 (интерпретируется как 12345.67), то:
SALARY-DISPLAYпокажет$12,345.67SECRET-AMOUNTпокажет**12,345.67
Редактируемые поля автоматически преобразуются при операции MOVE из числовых источников. Это позволяет отделить логику вычислений от логики представления.
Практические примеры использования типов данных
Пример 1: Банковская транзакция
01 BANK-TRANSACTION.
05 TX-ID PIC X(12).
05 TX-DATE PIC 9(8). *> ГГГГММДД
05 TX-AMOUNT PIC S9(9)V99.
05 TX-CURRENCY PIC X(3).
05 TX-DESCRIPTION PIC X(50).
88 VALID-CURRENCY VALUE 'USD', 'EUR', 'RUB'.
88 LARGE-AMOUNT VALUE 100000 THRU 99999999999.
Эта структура позволяет хранить все необходимые данные о переводе, проверять валюту и выявлять крупные суммы для дополнительной верификации.
Пример 2: Отчёт о продажах
01 SALES-REPORT-LINE.
05 PRODUCT-CODE PIC X(10).
05 QUANTITY PIC 9(5).
05 UNIT-PRICE PIC 9(5)V99.
05 TOTAL-AMOUNT PIC 9(7)V99.
01 DISPLAY-LINE.
05 FILLER PIC X(12) VALUE SPACES.
05 DISP-QTY PIC ZZZ,ZZ9.
05 FILLER PIC X(2) VALUE SPACES.
05 DISP-PRICE PIC $$$,$$9.99.
05 FILLER PIC X(2) VALUE SPACES.
05 DISP-TOTAL PIC $$$,$$$,$$9.99.
После вычисления TOTAL-AMOUNT = QUANTITY * UNIT-PRICE, значения копируются в DISPLAY-LINE для печати. Результат будет выглядеть как:
1,234 $123.45 $152,337.30
Такой подход обеспечивает чёткое разделение между данными и их представлением — принцип, который остаётся актуальным даже в современных архитектурах.
Совместимость и переносимость
COBOL стандартизирован ANSI и ISO, но разные компиляторы (IBM Enterprise COBOL, Micro Focus, GnuCOBOL и другие) могут по-разному интерпретировать некоторые детали, особенно в области редактируемых полей и представления знака. Например, положение знака в числах (S9(4)) может быть:
- Отдельным символом перед числом (
+1234) - Закодированным в последней цифре (так называемый trailing sign overpunch)
Поэтому при переносе кода между платформами требуется внимательная проверка форматов ввода-вывода.
Тем не менее, основной механизм PIC и групповых записей остаётся стабильным и совместимым на протяжении десятилетий. Это одна из причин, почему COBOL-системы продолжают работать без изменений с 1970-х годов.
Внутреннее представление данных в COBOL
Хотя COBOL абстрагирует программиста от деталей памяти через PIC-спецификации, знание того, как данные физически хранятся, важно для оптимизации, отладки и интеграции с другими системами.
DISPLAY — символьное представление
По умолчанию все поля в COBOL используют формат DISPLAY. Это означает, что каждая цифра или символ хранится как отдельный байт в кодировке EBCDIC (на мэйнфреймах) или ASCII (на других платформах). Например, число 12345 в поле PIC 9(5) занимает 5 байт, по одному на цифру.
Этот формат удобен для отладки и обмена данными с внешними системами, но неэффективен с точки зрения памяти и скорости вычислений.
COMPUTATIONAL (COMP) — двоичное представление
Для повышения производительности COBOL поддерживает компактные числовые форматы через использование USAGE IS COMPUTATIONAL (или сокращённо COMP).
- COMP-1 — одинарная точность с плавающей запятой (4 байта, IEEE 754).
- COMP-2 — двойная точность с плавающей запятой (8 байт).
- COMP-3 — упакованный десятичный формат (packed decimal), где две цифры хранятся в одном байте, а знак — в последней тетраде. Например, число
12345вPIC 9(5) COMP-3займёт всего 3 байта:12 34 5C(гдеC— код знака «плюс»).
Формат COMP-3 особенно распространён в финансовых системах, так как он сохраняет точность десятичных дробей без ошибок округления, присущих двоичным числам с плавающей запятой.
Пример объявления:
01 TOTAL-AMOUNT PIC S9(9)V99 USAGE COMP-3.
Такое поле займёт 6 байт и будет эффективно использоваться в арифметических операциях.
COMPUTATIONAL-4 (COMP-4) — двоичное целое
На некоторых платформах (например, IBM z/OS) используется COMP-4, где числа хранятся в двоичном виде, как в большинстве современных языков. Поле PIC S9(9) COMP-4 займёт 4 байта и позволит быстро выполнять сложение, умножение и другие операции.
Выбор формата зависит от требований к производительности, совместимости и точности. Важно помнить: COMP-поля нельзя напрямую использовать в операциях ввода-вывода — они должны быть преобразованы в DISPLAY-формат перед записью в файл или выводом на экран.
Работа с файлами и соответствие структуре записи
В COBOL данные часто читаются из и записываются в файлы с фиксированной или переменной длиной. Структура групповой записи должна точно соответствовать формату строки в файле.
Например, если файл содержит строки по 100 байт, где первые 10 байт — идентификатор, следующие 30 — имя, и последние 60 — описание, то запись будет выглядеть так:
01 CUSTOMER-FILE-RECORD.
05 CUST-ID PIC X(10).
05 CUST-NAME PIC X(30).
05 CUST-DESC PIC X(60).
При чтении файла с помощью READ вся строка автоматически разбивается по полям в соответствии с этой структурой. Это делает COBOL исключительно удобным для обработки legacy-данных, таких как плоские файлы, ленточные архивы или выгрузки из старых баз данных.
Если в файле используются упакованные числа (COMP-3), то соответствующие поля в записи также должны быть объявлены с USAGE COMP-3. Иначе произойдёт искажение данных.
Современное значение типов данных COBOL
Несмотря на возраст языка, его модель типов остаётся актуальной в контексте:
- Миграции legacy-систем: при переносе COBOL-приложений в облако или на микросервисную архитектуру важно точно воспроизвести семантику PIC-полей.
- Интеграции с API: современные системы часто получают данные в JSON или XML, но внутренняя логика может требовать преобразования в COBOL-форматы для совместимости с существующими модулями.
- Обучения и стандартизации: понимание PIC помогает разработчикам мыслить в терминах точности, масштаба и формата — навыки, полезные даже при работе с Python, Java или SQL.
Многие современные языки заимствовали идеи, заложенные в COBOL: например, концепция decimal в Python или BigDecimal в Java напрямую отражает потребность в точных десятичных вычислениях, которую COBOL решал с самого начала.