[COBOLのはじめ方] Part24: CSVファイルとの連携処理

COBOL

はじめに

今回のミニプロジェクトでは、COBOLプログラムとCSV(Comma-Separated Values)ファイルの連携処理を学びます。CSVファイルは、データをカンマ区切りで保存するシンプルなテキストファイル形式で、Excelなどの表計算ソフトや多くのシステムでデータ交換によく使われています。

COBOLプログラムでCSVファイルを読み書きできるようになると、他のシステムとのデータ連携がスムーズになり、活用の幅が広がります。 💪 さっそく、具体的な方法を見ていきましょう!

COBOLでのCSVファイルの扱い方

COBOLでCSVファイルを扱うには、主に以下の手順が必要です。

  1. ファイル定義 (ENVIRONMENT DIVISION, DATA DIVISION): CSVファイルをCOBOLプログラムで認識させるための定義を行います。
  2. ファイル操作 (PROCEDURE DIVISION): ファイルを開き (OPEN)、データを読み込み (READ) または書き込み (WRITE)、最後にファイルを閉じる (CLOSE) 処理を行います。
  3. データ編集 (PROCEDURE DIVISION): 読み込んだCSVデータを項目ごとに分解 (UNSTRING) したり、書き込むデータをCSV形式に組み立てたり (STRING) します。

1. ファイル定義

ENVIRONMENT DIVISIONFILE-CONTROL句で、プログラム内で使うファイル名(論理ファイル名)と実際のファイル名(物理ファイル名)を関連付けます。CSVファイルは通常、1行の長さが可変であるため、ORGANIZATION IS LINE SEQUENTIALを指定するのが一般的です。これは改行コードを区切りとしてレコードを扱うファイル形式です。

DATA DIVISIONFILE SECTIONで、FD句を使ってファイルのレコード構造を定義します。CSVファイルは1行全体のデータを格納する比較的大きな領域(例:PIC X(500))として定義し、行の実際の長さは別の変数(DEPENDING ON句で指定)で管理する方法があります。


 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT CSV-INPUT-FILE ASSIGN TO "input.csv"
         ORGANIZATION IS LINE SEQUENTIAL.
     SELECT OUTPUT-FILE ASSIGN TO "output.dat"
         ORGANIZATION IS LINE SEQUENTIAL.

 DATA DIVISION.
 FILE SECTION.
 FD CSV-INPUT-FILE.
* 可変長レコードとして扱う例
*    RECORD IS VARYING IN SIZE FROM 1 TO 500 CHARACTERS
*    DEPENDING ON CSV-REC-LEN.
* 固定長として大きな領域を確保する例(後でUNSTRINGで分解)
 01 CSV-INPUT-RECORD      PIC X(500).

* GnuCOBOLなどでは RECORD VARYING DEPENDING ON も使える
* FD CSV-INPUT-FILE RECORD VARYING DEPENDING ON REC-LENGTH.
* 01 CSV-INPUT-RECORD      PIC X(500).

 FD OUTPUT-FILE.
 01 OUTPUT-RECORD         PIC X(80).

 WORKING-STORAGE SECTION.
* 可変長レコードの長さを格納する変数 (FDで DEPENDING ON を使う場合)
* 01 REC-LENGTH            PIC 9(5) COMP.

* CSVレコードを分解して格納するデータ項目
 01 WS-CSV-DATA.
    05 WS-FIELD-1          PIC X(10).
    05 WS-FIELD-2          PIC X(20).
    05 WS-FIELD-3          PIC 9(5).

* CSVレコードを作成するためのデータ項目
 01 WS-OUTPUT-FIELDS.
    05 WS-OUT-FIELD-1      PIC X(10) VALUE "DATA-A".
    05 WS-OUT-FIELD-2      PIC X(20) VALUE "DATA-B".
    05 WS-OUT-FIELD-3      PIC 9(5) VALUE 12345.
      

2. ファイル操作とデータ編集

PROCEDURE DIVISIONで、実際のファイル操作とデータ編集を行います。

CSVファイルの読み込み (READとUNSTRING)

OPEN INPUTでCSVファイルを開き、READ文で1行ずつ読み込みます。読み込んだデータはFD句で定義したレコード領域(例: CSV-INPUT-RECORD)に格納されます。

読み込んだ1行のデータ(カンマ区切りの文字列)を個々の項目に分解するには、UNSTRING文を使います。DELIMITED BY ","でカンマを区切り文字として指定し、分解したデータをINTO句で指定した変数(例: WS-FIELD-1, WS-FIELD-2…)に格納します。


 PROCEDURE DIVISION.
 OPEN-FILES.
     OPEN INPUT CSV-INPUT-FILE.
     OPEN OUTPUT OUTPUT-FILE.

 READ-PROCESS-WRITE.
*   レコード領域を初期化してから読み込むのが安全
     MOVE SPACES TO CSV-INPUT-RECORD.
     READ CSV-INPUT-FILE
         AT END
             GO TO CLOSE-FILES
     END-READ.

*   読み込んだCSVレコードを分解
     UNSTRING CSV-INPUT-RECORD DELIMITED BY ","
         INTO WS-FIELD-1
              WS-FIELD-2
              WS-FIELD-3
*        必要に応じて TALLYING IN や COUNT IN を指定
     END-UNSTRING.

*   分解したデータを処理 (ここでは出力ファイルへ書き込む例)
     MOVE SPACES TO OUTPUT-RECORD. *> 出力レコード初期化
     STRING WS-FIELD-1 DELIMITED BY SIZE
            " "         DELIMITED BY SIZE *> 区切りスペース
            WS-FIELD-2 DELIMITED BY SIZE
            " "         DELIMITED BY SIZE
            WS-FIELD-3 DELIMITED BY SIZE *> 数値項目も転記
            INTO OUTPUT-RECORD.
     WRITE OUTPUT-RECORD.

*   次のレコードを処理するためにループ
     GO TO READ-PROCESS-WRITE.

 CLOSE-FILES.
     CLOSE CSV-INPUT-FILE
           OUTPUT-FILE.
     STOP RUN.
      

READ文を実行する前に、レコード領域(CSV-INPUT-RECORD)をMOVE SPACES TO ...などで初期化することが推奨されます。これにより、前のレコードのデータが残ってしまうのを防げます。

CSVファイルへの書き込み (STRINGとWRITE)

個々のデータ項目をカンマ区切りの1行の文字列に組み立てるには、STRING文を使います。DELIMITED BY句で各項目とカンマを指定し、結合した結果をINTO句で指定した出力レコード領域に格納します。

組み立てたデータをファイルに書き込むには、WRITE文を使用します。ORGANIZATION IS LINE SEQUENTIALで定義したファイルにWRITEすると、自動的に改行コードが付加されます。


 PROCEDURE DIVISION.
 OPEN-FILES.
     OPEN OUTPUT CSV-OUTPUT-FILE. *> CSV出力ファイルを開く

 WRITE-CSV-RECORD.
*    出力するデータ項目に値を設定 (例: WS-OUTPUT-FIELDS)
*    ...

*    データ項目をカンマ区切りで連結してCSVレコードを作成
     MOVE SPACES TO CSV-OUTPUT-RECORD. *> 出力レコード初期化
     STRING WS-OUT-FIELD-1 DELIMITED BY "  " *> 末尾スペース除去
            ","             DELIMITED BY SIZE
            WS-OUT-FIELD-2 DELIMITED BY "  "
            ","             DELIMITED BY SIZE
            WS-OUT-FIELD-3 DELIMITED BY SIZE *> 数値項目
         INTO CSV-OUTPUT-RECORD
*        必要に応じて WITH POINTER を指定
     END-STRING.

*    組み立てたCSVレコードをファイルに書き込む
     WRITE CSV-OUTPUT-RECORD.

*    他のレコードも書き込む場合は処理を繰り返す...

 CLOSE-FILES.
     CLOSE CSV-OUTPUT-FILE.
     STOP RUN.
      

STRING文で文字列項目を連結する際、DELIMITED BY " "(スペース2つ)やDELIMITED BY SIZEを使い分けることで、項目の前後の不要なスペースを取り除くことができます。数値項目は通常、DELIMITED BY SIZEでそのまま連結します。

サンプルプログラム例

ここでは、簡単なCSV読み込みと書き込みのサンプルを示します。

CSV読み込みプログラム (input.csv -> report.txt)

input.csv


1001,Yamada Taro,50000
1002,Sato Hanako,65000
1003,"Suzuki, Jiro",72000
      

read_csv.cbl


 IDENTIFICATION DIVISION.
 PROGRAM-ID. READ-CSV-SAMPLE.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT CSV-FILE ASSIGN TO "input.csv"
         ORGANIZATION IS LINE SEQUENTIAL.
     SELECT REPORT-FILE ASSIGN TO "report.txt"
         ORGANIZATION IS LINE SEQUENTIAL.
 DATA DIVISION.
 FILE SECTION.
 FD CSV-FILE.
 01 CSV-RECORD           PIC X(100).
 FD REPORT-FILE.
 01 REPORT-RECORD        PIC X(80).
 WORKING-STORAGE SECTION.
 01 WS-CSV-FIELDS.
    05 WS-ID              PIC X(4).
    05 WS-NAME            PIC X(20).
    05 WS-SALARY          PIC 9(7). *> UNSTRINGでは数値変換しない想定
 01 WS-WORK-AREAS.
    05 WS-SALARY-DISP     PIC ZZZ,ZZ9.
    05 WS-EOF-FLAG        PIC X VALUE 'N'.
       88 EOF             VALUE 'Y'.

 PROCEDURE DIVISION.
 MAIN-PROCEDURE.
     OPEN INPUT CSV-FILE
          OUTPUT REPORT-FILE.

     PERFORM UNTIL EOF
         READ CSV-FILE
             AT END
                 SET EOF TO TRUE
             NOT AT END
                 PERFORM PROCESS-RECORD
         END-READ
     END-PERFORM.

     CLOSE CSV-FILE
           REPORT-FILE.
     STOP RUN.

 PROCESS-RECORD.
*    CSVレコードを分解 (簡易的な処理)
     UNSTRING CSV-RECORD DELIMITED BY ","
         INTO WS-ID
              WS-NAME
              WS-SALARY *> 文字列として受け取る
     END-UNSTRING.

*    レポート形式に編集して出力
     MOVE WS-SALARY TO WS-SALARY-DISP. *> 表示用に編集
     MOVE SPACES TO REPORT-RECORD.
     STRING "ID:"      DELIMITED BY SIZE
            WS-ID      DELIMITED BY "  "
            ", Name:" DELIMITED BY SIZE
            WS-NAME    DELIMITED BY "  "
            ", Salary:" DELIMITED BY SIZE
            WS-SALARY-DISP DELIMITED BY " " *> 先頭スペース除去
         INTO REPORT-RECORD.
     WRITE REPORT-RECORD.
      

report.txt (実行結果)


ID:1001, Name:Yamada Taro       , Salary:  50,000
ID:1002, Name:Sato Hanako        , Salary:  65,000
ID:1003, Name:"Suzuki, Jiro"     , Salary:  72,000
      

CSV書き込みプログラム (data -> output.csv)

write_csv.cbl


 IDENTIFICATION DIVISION.
 PROGRAM-ID. WRITE-CSV-SAMPLE.
 ENVIRONMENT DIVISION.
 INPUT-OUTPUT SECTION.
 FILE-CONTROL.
     SELECT CSV-OUT-FILE ASSIGN TO "output.csv"
         ORGANIZATION IS LINE SEQUENTIAL.
 DATA DIVISION.
 FILE SECTION.
 FD CSV-OUT-FILE.
 01 CSV-OUT-RECORD       PIC X(100).
 WORKING-STORAGE SECTION.
 01 WS-PRODUCT-DATA.
    05 PRODUCT-CODE       PIC X(5).
    05 PRODUCT-NAME       PIC X(30).
    05 PRICE              PIC 9(6).
    05 STOCK-QTY          PIC 9(4).

 PROCEDURE DIVISION.
 MAIN-PROCEDURE.
     OPEN OUTPUT CSV-OUT-FILE.

*    サンプルデータ1の書き込み
     MOVE "P0001" TO PRODUCT-CODE.
     MOVE "High Quality Widget" TO PRODUCT-NAME.
     MOVE 15000 TO PRICE.
     MOVE 120 TO STOCK-QTY.
     PERFORM CREATE-AND-WRITE-CSV.

*    サンプルデータ2の書き込み (名前にカンマを含む例)
     MOVE "P0002" TO PRODUCT-CODE.
     MOVE 'Standard Gadget, Model A' TO PRODUCT-NAME.
     MOVE 8500 TO PRICE.
     MOVE 300 TO STOCK-QTY.
     PERFORM CREATE-AND-WRITE-CSV.

     CLOSE CSV-OUT-FILE.
     STOP RUN.

 CREATE-AND-WRITE-CSV.
*    データ項目をCSV形式に組み立てる
     MOVE SPACES TO CSV-OUT-RECORD.
     STRING FUNCTION TRIM(PRODUCT-CODE) DELIMITED BY SIZE
            "," DELIMITED BY SIZE
*           カンマを含む可能性のある項目はダブルクォートで囲む
            '"' FUNCTION TRIM(PRODUCT-NAME) '"' DELIMITED BY SIZE
            "," DELIMITED BY SIZE
            PRICE DELIMITED BY SIZE *> 数値はそのまま
            "," DELIMITED BY SIZE
            STOCK-QTY DELIMITED BY SIZE
         INTO CSV-OUT-RECORD.
*    ファイルに書き込み
     WRITE CSV-OUT-RECORD.
      

output.csv (実行結果)


P0001,"High Quality Widget",15000,120
P0002,"Standard Gadget, Model A",008500,300
      

上記のサンプルは基本的な例です。実際のCSV処理では、以下のような点を考慮する必要があります。

  • ダブルクォーテーションの扱い: フィールド内にカンマやダブルクォーテーション自体が含まれる場合、フィールド全体をダブルクォーテーションで囲むのが一般的です。読み込み時にはこれらの処理、書き込み時には適切な引用符付けが必要です。UNSTRINGSTRINGだけでは複雑なケースに対応しきれない場合があり、1文字ずつ検査するロジックが必要になることもあります。
  • 文字コード: CSVファイルの文字コード(Shift_JIS, UTF-8など)とCOBOLプログラムが扱う文字コードが異なる場合、文字化けが発生する可能性があります。必要に応じて文字コード変換処理を組み込むか、コンパイラのオプションで対応します。
  • 空のフィールド: CSVファイルでは,,のようにフィールドが空の場合があります。UNSTRINGでこれらを正しく処理できるか確認が必要です(受け取る変数を適切に初期化するなど)。
  • エラーハンドリング: ファイルI/Oエラー(ファイルが見つからない、書き込み権限がないなど)や、データ形式エラー(数値項目に文字が入っているなど)に対するエラー処理を実装することが重要です。FILE STATUS句やINVALID KEY句、AT END句などを活用します。

まとめ

今回は、COBOLプログラムでCSVファイルを扱う基本的な方法を学びました。UNSTRING文によるデータの分解とSTRING文によるデータの組み立てが中心となります。

CSVファイルは、異なるシステム間でデータをやり取りするための重要な形式です。COBOLでCSV連携ができるようになれば、既存のCOBOL資産を活かしつつ、他のシステムとの連携を強化できます。🎉

実際の業務では、より複雑なCSVフォーマットやエラー処理に対応する必要がありますが、今回の内容がその第一歩となれば幸いです。ぜひ、ご自身の環境でサンプルプログラムを動かしてみてください!

参考情報

※ 各COBOL処理系の正確な文法や仕様については、必ず公式のマニュアルをご確認ください。

コメント

タイトルとURLをコピーしました