Site logo
Tác giả
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Ngày xuất bản
Ngày xuất bản

FILE-CONTROL và FILE SECTION trong COBOL – Đọc file text tuần tự như batch program thực tế

Vì sao phải hiểu FILE-CONTROL và FILE SECTION trong COBOL?

Trong các bài trước, bạn đã quen với:

  • Bài 002: DATA DIVISION, level number, PIC
  • Bài 004: Toán tử & biểu thức
  • Bài 005: IF, EVALUATE
  • Bài 006: Vòng lặp & PERFORM

Nhưng một batch program thực tế (như RZZBSQLN1.PCO) luôn có thêm một mảnh ghép quan trọng:

Đọc/ghi file – thường là file text tuần tự, dùng để nhận input hoặc ghi log/output.

Trong COBOL, phần này được mô tả bởi hai khu vực chính:

  • ENVIRONMENT DIVISION → INPUT-OUTPUT SECTION → FILE-CONTROL – định nghĩa file logic, liên kết với file vật lý, tổ chức file, FILE STATUS
  • DATA DIVISION → FILE SECTION – mô tả cấu trúc record (layout từng dòng trong file)

Bài này sẽ giúp bạn:

  • Đọc được các đoạn như: SELECT INP1FILE ASSIGN TO "INP1-MSD" ... FILE STATUS IS F_STS.
  • Hiểu vai trò của FD, 01 INP1_REC, READ ... AT END ... NOT AT END ...
  • Mapping sang mental model Java/Python (FileReader, BufferedReader)
  • Viết được một skeleton batch để đọc file tuần tự từ đầu đến cuối

Tổng quan: ENVIRONMENT DIVISION, FILE-CONTROL và FILE SECTION

Trong một chương trình COBOL xử lý file, bạn thường thấy cấu trúc như:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SAMPLE-FILE.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT INFILE ASSIGN TO "input.txt"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-FILE-STS.

       DATA DIVISION.
       FILE SECTION.
       FD  INFILE.
       01  IN-REC.
           05 IN-FIELD1  PIC X(10).
           05 IN-FIELD2  PIC 9(5).

       WORKING-STORAGE SECTION.
       01  WS-FILE-STS  PIC X(2) VALUE "00".
       01  WS-END-FLAG  PIC 9    VALUE 0.

       PROCEDURE DIVISION.
           OPEN INPUT INFILE
           PERFORM UNTIL WS-END-FLAG = 1
               READ INFILE
                   AT END
                       MOVE 1 TO WS-END-FLAG
                   NOT AT END
                       DISPLAY IN-FIELD1 IN-FIELD2
               END-READ
           END-PERFORM
           CLOSE INFILE
           STOP RUN.

Tách component:

  • FILE-CONTROL:
    • Khai báo tên file logic (INFILE)
    • Liên kết với file vật lý (ASSIGN TO "input.txt")
    • Định nghĩa cách tổ chức file (ORGANIZATION IS LINE SEQUENTIAL)
    • Chỉ định biến chứa status (FILE STATUS IS WS-FILE-STS)
  • FILE SECTION:
    • FD INFILE. – mô tả file
    • 01 IN-REC. – mô tả một record (mỗi dòng trong file)
  • PROCEDURE DIVISION:
    • OPEN INPUT INFILE
    • READ INFILE AT END ... NOT AT END ...
    • CLOSE INFILE

Trong RZZBSQLN1.PCO, pattern này xuất hiện với:

SELECT INP1FILE ASSIGN TO "INP1-MSD"
    ORGANIZATION IS LINE SEQUENTIAL
    FILE STATUS IS F_STS.

FD  INP1FILE
    LABEL RECORDS ARE STANDARD.
01  INP1_REC.
    02  INP1_SQL    PIC X(80).

Đây là file chứa các câu lệnh SQL text, mỗi dòng một SQL.


FILE-CONTROL – SELECT, ASSIGN, ORGANIZATION, FILE STATUS

1. SELECT và ASSIGN – đặt tên logic cho file

FILE-CONTROL.
    SELECT INP1FILE ASSIGN TO "INP1-MSD".
  • INP1FILEtên file logic trong chương trình COBOL
  • ASSIGN TO "INP1-MSD"tên file vật lý (tùy platform):
    • Trên mainframe: có thể là DDNAME, dataset name
    • Trên GnuCOBOL: có thể là đường dẫn "/path/input.txt"

Mapping sang Java pseudo-code:

BufferedReader inp1file = new BufferedReader(
    new FileReader("INP1-MSD")
);

Bạn làm việc với INP1FILE (logic name), còn runtime sẽ hiểu file vật lý tương ứng.

2. ORGANIZATION IS LINE SEQUENTIAL – file text từng dòng

ORGANIZATION IS LINE SEQUENTIAL

Ý nghĩa:

  • File được xem như danh sách các dòng text, mỗi lần READ đọc một dòng
  • Mỗi record trong FILE SECTION tương ứng với một line

Các giá trị khác của ORGANIZATION (nâng cao hơn, dùng cho VSAM, indexed file):

  • SEQUENTIAL
  • RELATIVE
  • INDEXED

Trong context batch đọc file text đơn giản, bạn hầu như sẽ dùng LINE SEQUENTIAL.

3. FILE STATUS IS – lưu mã lỗi I/O

FILE STATUS IS F_STS.
  • F_STS là biến PIC X(2) trong WORKING-STORAGE, ví dụ:

    77  F_STS  PIC X(2) VALUE SPACE.
    
  • Mỗi lần OPEN, READ, CLOSE file, COBOL sẽ set F_STS:

    • "00" – OK
    • Các giá trị khác – lỗi hoặc trạng thái đặc biệt (EOF, permission, không tìm thấy file, ...)

Trong RZZBSQLN1, bạn thấy pattern:

IF F_STS NOT = "00"
    ... xử lý lỗi file ...
END-IF.

Về tư duy, có thể coi như:

int status = openFile();
if (status != 0) {
    // handle error
}

FILE SECTION – FD và record layout

Sau FILE-CONTROL, tới DATA DIVISION → FILE SECTION:

FILE SECTION.
FD  INP1FILE.
01  INP1_REC.
    02 INP1_SQL PIC X(80).

Ý nghĩa:

  • FD INP1FILE. – khai báo file description cho logic name INP1FILE
  • 01 INP1_REC. – mô tả một dòng (record) trong file
  • Bên trong INP1_REC là các field con (level 02, 05,...)

Ví dụ khác, file chứa thông tin khách hàng:

FD  CUSTFILE.
01  CUST-REC.
    05 CUST-ID      PIC 9(5).
    05 CUST-NAME    PIC X(30).
    05 CUST-AGE     PIC 9(3).

Nếu ORGANIZATION IS LINE SEQUENTIAL, mỗi dòng trong file text sẽ map vào CUST-REC theo layout này:

  • Từ cột 1–5 : CUST-ID
  • Từ cột 6–35 : CUST-NAME
  • Từ cột 36–38: CUST-AGE

File thực tế có thể trông như:

00001Nguyen Van A                 025
00002Tran Thi B                   031

Khi READ CUSTFILE, COBOL sẽ:

  • Lấy 5 ký tự đầu → CUST-ID
  • Lấy 30 ký tự tiếp → CUST-NAME
  • Lấy 3 ký tự cuối → CUST-AGE

01 CUST-REC chính là record cấp 01 mà bạn đã học ở bài 006 (level number).


OPEN, READ, CLOSE – vòng đời làm việc với file

1. OPEN – mở file với mode thích hợp

OPEN INPUT  INFILE.
OPEN OUTPUT OUTFILE.
OPEN I-O    UPDFILE.

Các mode cơ bản:

  • INPUT – chỉ đọc
  • OUTPUT – chỉ ghi (ghi mới)
  • I-O – đọc/ghi (update)
  • EXTEND – ghi nối thêm cuối file

Sau OPEN, bạn phải check FILE STATUS nếu được khai báo.

2. READ – đọc từng record

Form điển hình với AT END:

READ INFILE
    AT END
        ... xử lý khi hết file ...
    NOT AT END
        ... xử lý record đọc được ...
END-READ.

Kết hợp với loop (như đã thấy ở bài 006 – PERFORM):

PERFORM UNTIL WS-END-FLAG = 1
    READ INFILE
        AT END
            MOVE 1 TO WS-END-FLAG
        NOT AT END
            ... xử lý record ...
    END-READ
END-PERFORM.

Đây là pattern main loop chuẩn để đọc file từ đầu tới EOF.

3. CLOSE – đóng file

CLOSE INFILE.

Thông thường, bạn nên đóng file trong paragraph riêng (ví dụ INP1-CLOSE-RTN trong RZZBSQLN1) và chỉ gọi một lần ở cuối chương trình hoặc khi có error cần stop.


Mapping mental model sang Java/Python

Giả sử bạn có đoạn COBOL đọc file khách hàng:

FILE-CONTROL.
    SELECT CUSTFILE ASSIGN TO "customer.txt"
        ORGANIZATION IS LINE SEQUENTIAL
        FILE STATUS IS F_STS.

FILE SECTION.
FD  CUSTFILE.
01  CUST-REC.
    05 CUST-ID    PIC 9(5).
    05 CUST-NAME  PIC X(30).

WORKING-STORAGE SECTION.
77  F_STS         PIC X(2) VALUE SPACE.
77  END-FLAG      PIC 9    VALUE 0.

PROCEDURE DIVISION.
    OPEN INPUT CUSTFILE
    IF F_STS NOT = "00"
        DISPLAY "FILE OPEN ERROR: " F_STS
        STOP RUN
    END-IF

    PERFORM UNTIL END-FLAG = 1
        READ CUSTFILE
            AT END
                MOVE 1 TO END-FLAG
            NOT AT END
                DISPLAY "ID=" CUST-ID " NAME=" CUST-NAME
        END-READ
    END-PERFORM

    CLOSE CUSTFILE
    STOP RUN.

Java-style pseudo-code tương đương:

try (BufferedReader reader = new BufferedReader(new FileReader("customer.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        String custId   = line.substring(0, 5);
        String custName = line.substring(5, 35);
        System.out.println("ID=" + custId + " NAME=" + custName);
    }
} catch (IOException e) {
    System.out.println("FILE OPEN/READ ERROR" + e.getMessage());
}

Điểm khác biệt:

  • COBOL mô tả layout ngay trong code (FILE SECTION)
  • Java/Python thường phải tự substring/split để parse

Pattern trong RZZBSQLN1: INP1FILE và record SQL

Trích đoạn (rút gọn) từ RZZBSQLN1.PCO:

FILE-CONTROL.
    SELECT INP1FILE ASSIGN TO "INP1-MSD"
        ORGANIZATION IS LINE SEQUENTIAL
        FILE STATUS IS F_STS.

FILE SECTION.
FD  INP1FILE
    LABEL RECORDS ARE STANDARD.
01  INP1_REC.
    02  INP1_SQL   PIC X(80).

Và đoạn đọc file (giản lược):

INP1-OPEN-RTN.
    OPEN INPUT INP1FILE
    IF F_STS NOT = "00"
        ... log lỗi ...
        GO TO STOP-PGM
    END-IF.

INP1-INPUT-RTN.
    READ INP1FILE
        AT END
            MOVE 1 TO INP1_END
        NOT AT END
            ADD 1 TO INP1_INPCNT
    END-READ.

Tư duy backend dev khi đọc:

  • INP1FILE ~ file chứa danh sách SQL text
  • INP1_REC ~ một dòng trong file, chứa INP1_SQL
  • INP1-OPEN-RTN ~ mở file, check status
  • INP1-INPUT-RTN ~ đọc từng dòng, tăng counter, set flag EOF

Trong các bài sau về embedded SQL, bạn sẽ thấy các dòng này được đưa vào table WK_SQL_TBL và thực thi bằng EXECUTE IMMEDIATE.


Ví dụ tổng hợp: chương trình đọc file text đơn giản

Dưới đây là ví dụ full một chương trình COBOL đọc file text và in từng dòng cùng số thứ tự record.

File input (data/customer.txt)

00001Nguyen Van A                 025
00002Tran Thi B                   031
00003Le Van C                     040

Chương trình COBOL

       IDENTIFICATION DIVISION.
       PROGRAM-ID. READ-CUSTOMER.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT CUSTFILE ASSIGN TO "customer.txt"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS F-ST.

       DATA DIVISION.
       FILE SECTION.
       FD  CUSTFILE.
       01  CUST-REC.
           05 CUST-ID      PIC 9(5).
           05 CUST-NAME    PIC X(30).
           05 CUST-AGE     PIC 9(3).

       WORKING-STORAGE SECTION.
       77  F-ST           PIC X(2) VALUE SPACE.
       77  END-FLAG       PIC 9    VALUE 0.
       77  REC-COUNT      PIC 9(5) VALUE 0.

       PROCEDURE DIVISION.
       MAIN-RTN.
           PERFORM OPEN-RTN THRU OPEN-EXT
           PERFORM READ-RTN THRU READ-EXT
               UNTIL END-FLAG = 1
           PERFORM CLOSE-RTN THRU CLOSE-EXT
           STOP RUN.

       OPEN-RTN.
           OPEN INPUT CUSTFILE
           IF F-ST NOT = "00"
               DISPLAY "FILE OPEN ERROR: " F-ST
               MOVE 1 TO END-FLAG
           END-IF.

       OPEN-EXT.
           EXIT.

       READ-RTN.
           READ CUSTFILE
               AT END
                   MOVE 1 TO END-FLAG
               NOT AT END
                   ADD 1 TO REC-COUNT
                   DISPLAY "REC#" REC-COUNT
                   DISPLAY "  ID  : " CUST-ID
                   DISPLAY "  NAME: " CUST-NAME
                   DISPLAY "  AGE : " CUST-AGE
           END-READ.

       READ-EXT.
           EXIT.

       CLOSE-RTN.
           CLOSE CUSTFILE
           DISPLAY "TOTAL RECORD = " REC-COUNT.

       CLOSE-EXT.
           EXIT.

Ví dụ này kết hợp:

  • FILE-CONTROL + FILE SECTION
  • OPEN/READ/CLOSE
  • FILE STATUS
  • PERFORM ... THRU ... + PERFORM ... UNTIL ...

Tức là gắn kết những gì bạn đã học ở bài 004–006 với file I/O thực tế.


Bài tập thực hành

Bài 1 – Skeleton đọc file sản phẩm

Thiết kế chương trình đọc file sản phẩm với layout:

  • PROD-ID – 10 ký tự (X(10))
  • PROD-NAME – 30 ký tự (X(30))
  • PROD-PRICE– 9 ký tự numeric (9(9))

Yêu cầu:

  • Khai báo FILE-CONTROL với SELECT, ASSIGN TO "product.txt", ORGANIZATION IS LINE SEQUENTIAL, FILE STATUS IS WS-FS.
  • Khai báo FILE SECTION với FD PROD-FILE01 PROD-REC tương ứng.
  • Viết PROCEDURE DIVISION tối thiểu có:
    • OPEN INPUT PROD-FILE
    • Loop READ đến EOF
    • Mỗi record: DISPLAY ID + NAME + PRICE
    • CLOSE PROD-FILE

Không cần chạy được ngay, chỉ cần đúng cấu trúc.

Bài 2 – Đếm số record và xử lý lỗi FILE STATUS

Mở rộng bài 1:

  • Thêm WS-REC-COUNT (9(5)) và WS-ERR-FLAG (9)
  • Logic:
    • Sau OPEN, nếu WS-FS NOT = "00" → set WS-ERR-FLAG = 1, STOP RUN
    • Khi READFILE STATUS != "00" và != EOF (tùy platform) → tăng ERR-COUNT, hiển thị message
    • Cuối chương trình, hiển thị tổng record đọc được và tổng số lỗi I/O

Bài 3 – Mapping FILE SECTION sang Java parser

Lấy lại layout CUST-REC ở ví dụ trên. Hãy viết pseudo-code Java hoặc Python để:

  • Mở file text
  • Đọc từng dòng
  • Parse custId, custName, custAge đúng theo vị trí

Mục tiêu: luyện thói quen luôn mapping COBOL layout sang tư duy parsing hiện đại.

Bài 4 – Áp dụng pattern của RZZBSQLN1

Từ các trích đoạn về INP1FILE trong RZZBSQLN1.PCO, hãy:

  • Viết lại FILE-CONTROL + FILE SECTION dưới dạng rút gọn (chỉ giữ phần liên quan đến INP1_SQL)
  • Viết pseudo-code mô tả INP1-OPEN-RTNINP1-INPUT-RTN bằng Java hoặc Python
  • Chỉ ra:
    • Biến nào là FILE STATUS
    • Biến nào là flag EOF
    • Biến nào là counter số record đọc được

Kết luận

Sau bài 007, bạn đã nắm được:

  • Cách COBOL định nghĩa file logic và vật lý qua FILE-CONTROL
  • Cách mô tả layout từng dòng file bằng FILE SECTION / FD / 01 record
  • Pattern chuẩn để đọc file tuần tự: OPENREAD ... AT END ... trong loop → CLOSE
  • Liên hệ trực tiếp với code enterprise như RZZBSQLN1.PCO

Kết hợp với kiến thức trước đó (DATA DIVISION, level number, PERFORM loop, IF/EVALUATE), bạn đã có thể tự tin đọc, hiểu và viết các chương trình COBOL đơn giản để xử lý file text – nền tảng của rất nhiều batch job ngoài đời.

Ở các bài tiếp theo, chúng ta sẽ:

  • Đi sâu vào WORKING-STORAGE nâng cao với OCCURS và REDEFINES – cách khai báo table và struct phức tạp (bài 008)
  • Phân tích cách RZZBSQLN1 dùng WK_SQL_TBLWK_PARM_TBL để lưu danh sách SQL/parameter

Từ đó, bạn dần tiến tới việc đọc trọn vẹn logic của một chương trình COBOL lớn và có thể tự thiết kế batch program cho hệ thống của riêng mình.