FILE SECTION: чтение и запись файлов
Разбираемся, как COBOL-программа читает файл с данными и создаёт новый — на примере обработки списка клиентов банка.
Файл записей — это набор данных на диске, где каждая «строка» (запись) имеет одинаковую структуру: одни и те же поля в одном и том же порядке.
Зачем это нужно
Представь банк, в котором миллионы счетов. Все данные о счетах не живут в оперативной памяти — они хранятся в файлах на дисках. Ночью, когда клиенты не проводят операции, специальные COBOL-программы («батч-задания») читают эти файлы, начисляют проценты, применяют комиссии и сохраняют результат обратно. Это называется пакетная обработка (batch processing), и именно с ней COBOL справляется лучше почти любого другого языка — он придуман для потоковой работы с большими файлами записей.
Поэтому работа с файлами в COBOL — не факультативная тема, а, можно сказать, весь смысл существования языка. Разберёмся, из каких шагов это состоит.
Файл описывается дважды
В COBOL про каждый файл нужно рассказать компилятору дважды: сначала — где физически лежит файл и как к нему обращаться (в ENVIRONMENT DIVISION), потом — как устроена одна запись внутри файла, из каких полей она состоит (в FILE SECTION, которая является частью DATA DIVISION).
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CLIENT-FILE ASSIGN TO "clients.dat"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD CLIENT-FILE.
01 CLIENT-RECORD.
05 CLIENT-ID PIC 9(6).
05 CLIENT-NAME PIC X(20).
05 CLIENT-BALANCE PIC 9(7)V99.Разберём построчно. SELECT CLIENT-FILE ASSIGN TO "clients.dat" — говорим: «файл, с которым я буду работать под именем CLIENT-FILE, физически лежит в файле clients.dat». ORGANIZATION IS LINE SEQUENTIAL — это самый простой тип файла: записи идут одна за другой, как строки в текстовом файле (в реальных банковских системах чаще используют ORGANIZATION IS INDEXED с быстрым поиском по ключу, но для знакомства с темой хватит и последовательного файла).
FD CLIENT-FILE (File Description) открывает описание структуры записи. Дальше — привычный по прошлым темам курса уровень 01 и вложенные поля 05: у каждого клиента есть CLIENT-ID (шестизначный номер), CLIENT-NAME (текст до 20 символов) и CLIENT-BALANCE (баланс с двумя цифрами после запятой — помнишь V99 из темы про данные? Это подразумеваемая десятичная точка).
Четыре глагола на весь жизненный цикл файла
Работа с файлом в COBOL укладывается всего в четыре команды, и они всегда идут в одном порядке: открыть → читать/писать → закрыть.
| Команда | Что делает |
OPEN INPUT / OUTPUT | открывает файл на чтение или на запись (перезаписывает файл целиком) |
READ | читает следующую запись из открытого файла |
WRITE | записывает одну запись в открытый файл |
CLOSE | закрывает файл, сохраняя все изменения на диск |
Обрати внимание: файл нужно явно открыть, указав режим — INPUT (только читать) или OUTPUT (только писать, причём если файл уже существовал, он будет перезаписан с нуля). Есть ещё режим I-O, когда файл и читают, и правят «на месте», но с ним разберёмся в других разделах курса — пока сосредоточимся на базовом цикле.
Цикл чтения файла до конца
Самая частая задача — прочитать файл целиком, обрабатывая каждую запись по очереди, пока файл не кончится. Для этого используют флаг конца файла (end-of-file, сокращённо EOF) и глагол READ ... AT END.
WORKING-STORAGE SECTION.
01 WS-EOF-FLAG PIC X VALUE "N".
PROCEDURE DIVISION.
MAIN-LOGIC.
OPEN INPUT CLIENT-FILE.
PERFORM UNTIL WS-EOF-FLAG = "Y"
READ CLIENT-FILE
AT END
MOVE "Y" TO WS-EOF-FLAG
NOT AT END
DISPLAY CLIENT-NAME " -- " CLIENT-BALANCE
END-READ
END-PERFORM.
CLOSE CLIENT-FILE.
STOP RUN.Смотри, как это работает. Сначала объявляем в WORKING-STORAGE SECTION обычную переменную-флаг WS-EOF-FLAG со значением "N" («нет, файл ещё не кончился»). Открываем файл на чтение — OPEN INPUT CLIENT-FILE. Дальше цикл PERFORM UNTIL: «выполняй, пока флаг не станет "Y"».
Внутри цикла — READ CLIENT-FILE. У этой команды две ветки исхода. AT END срабатывает ровно один раз — когда записи в файле закончились: тогда мы ставим флаг "Y", и на следующей проверке условия цикл остановится. NOT AT END срабатывает при каждом успешном чтении: запись прочиталась, поля CLIENT-RECORD заполнены свежими данными, и мы можем их использовать — здесь просто выводим имя и баланс через DISPLAY. END-READ закрывает условную конструкцию, как END-IF закрывает IF.
После цикла — обязательно CLOSE CLIENT-FILE. Если про закрытие забыть, часть данных может не сохраниться на диск (это касается записи), а сам файл может остаться заблокированным для других программ.
Запись в новый файл
Симметрично устроена и запись. Допустим, мы хотим сохранить в отдельный файл только клиентов с балансом больше нуля.
OPEN INPUT CLIENT-FILE.
OPEN OUTPUT ACTIVE-CLIENTS-FILE.
PERFORM UNTIL WS-EOF-FLAG = "Y"
READ CLIENT-FILE
AT END
MOVE "Y" TO WS-EOF-FLAG
NOT AT END
IF CLIENT-BALANCE > 0
WRITE ACTIVE-RECORD FROM CLIENT-RECORD
END-IF
END-READ
END-PERFORM.
CLOSE CLIENT-FILE.
CLOSE ACTIVE-CLIENTS-FILE.Здесь открыты сразу два файла: один на чтение (CLIENT-FILE), другой на запись (ACTIVE-CLIENTS-FILE) — это нормально, COBOL-программа может держать открытыми много файлов одновременно. Внутри цикла читаем запись как раньше, а если баланс положительный — вызываем WRITE ACTIVE-RECORD FROM CLIENT-RECORD: записываем в выходной файл содержимое, взятое из только что прочитанной записи. Оба файла закрываются в конце — порядок закрытия не важен, но забывать про него нельзя ни для одного из них.
Вывод (то, что программа напечатает через DISPLAY на примере с тремя клиентами в файле):
Иванов И.И. -- 0015000.50
Петров П.П. -- 0000320.00
Сидорова А.А. -- 0198450.75Частые ошибки
- Забыть открыть файл перед READ/WRITE. Программа завершится с ошибкой файлового статуса — COBOL не разрешает работать с закрытым файлом.
- Перепутать режим OPEN. Если открыть файл, который нужно читать, в режиме
OUTPUT, COBOL послушно сотрёт его содержимое и начнёт с чистого листа — данные будут потеряны. - Забыть флаг конца файла или не проверить его в условии PERFORM. Тогда после того, как записи закончатся, программа попытается читать «в пустоту» ещё раз, что тоже считается ошибкой.
- Не закрыть файл в конце программы. Особенно опасно для записи: часть данных может остаться «застрявшей» в буфере и не попасть на диск.
Итоги
- Файл описывается дважды: физическое расположение — в
FILE-CONTROL, структура записи — вFILE SECTIONсFD. - Жизненный цикл файла — всегда
OPEN→ серияREADилиWRITE→CLOSE. - Чтение до конца файла делают через флаг и связку
PERFORM UNTIL+READ ... AT END / NOT AT END. - Именно этот шаблон лежит в основе ночной пакетной обработки в банках: прочитать миллионы записей, обработать, записать результат.