[COBOLのはじめ方] Part12: 文字列処理(STRING, UNSTRING)


Warning: Undefined array key “is_admin” in /home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line 113

Warning: Undefined array key “is_category_top” in /home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line 118

Warning: Undefined array key “is_top” in /home/c2261046/public_html/omomuki-tech.com/wp-content/themes/sango-theme/library/gutenberg/dist/classes/Toc.php on line 124

はじめに

COBOLプログラミングの世界へようこそ!😊 Step 3では、COBOLの基本的な制御構文と演算処理を学んできましたね。今回は、その中でも特に実務でよく使われる「文字列処理」に焦点を当てます。

COBOLでは、文字列を連結したり、逆に特定のルールで分割したりする操作が頻繁に必要になります。例えば、バラバラの項目(姓と名など)を組み合わせて表示用の氏名を作成したり、CSVファイルのようにカンマで区切られたデータを個別の項目に分解したりする場合です。

このような文字列操作を実現するために、COBOLには強力な命令文である STRING 文と UNSTRING 文が用意されています。これらの文を使いこなすことで、データ加工の幅がぐっと広がります。

このセクションでは、STRING 文と UNSTRING 文の基本的な使い方から、便利なオプション句まで、サンプルコードを交えながら分かりやすく解説していきます。しっかりマスターして、COBOLでのデータハンドリング能力を高めましょう!💪

STRING文:文字列を連結する

STRING 文は、複数の文字列やデータ項目を連結して、一つの文字列変数に格納するための命令文です。ログメッセージの作成や、画面表示用の整形済み文字列の生成など、様々な場面で活用されます。

基本的な構文は以下のようになります。


STRING 送り出し項目-1 [DELIMITED BY 区切り文字-1 | SIZE]
       送り出し項目-2 [DELIMITED BY 区切り文字-2 | SIZE]
       ...
    INTO 受け取り項目
    [WITH POINTER ポインタ変数]
    [ON OVERFLOW 命令文-1]
    [NOT ON OVERFLOW 命令文-2]
END-STRING
      
ポイント💡
  • 送り出し項目: 連結したい文字列やデータ項目(変数やリテラル)を指定します。
  • DELIMITED BY: どこまでを連結対象とするかを指定します。
    • 区切り文字: 指定した文字が現れる直前までを連結します。区切り文字自体は連結されません。
    • SIZE: 送り出し項目の定義サイズ全体を連結します。指定しない場合のデフォルトです。
  • INTO: 連結結果を格納する変数(受け取り項目)を指定します。
  • END-STRING: STRING文の終わりを示します(明示的な範囲指定)。

基本的な使い方 (例1: 文字列リテラルの連結)

まずは簡単な例を見てみましょう。姓と名を連結して氏名を作成します。


IDENTIFICATION DIVISION.
PROGRAM-ID. STRING-SAMPLE1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-LAST-NAME    PIC X(10) VALUE '山田'.
01 WS-FIRST-NAME   PIC X(10) VALUE '太郎'.
01 WS-FULL-NAME    PIC X(21). *> 姓 + スペース + 名
PROCEDURE DIVISION.
  STRING WS-LAST-NAME DELIMITED BY SPACE  *> '山田'の後続スペースは除外
         ' '           DELIMITED BY SIZE  *> 半角スペースを連結
         WS-FIRST-NAME DELIMITED BY SPACE  *> '太郎'の後続スペースは除外
      INTO WS-FULL-NAME
  END-STRING.

  DISPLAY '氏名: ' WS-FULL-NAME. *> 結果: 氏名: 山田 太郎

  STOP RUN.
      

この例では、WS-LAST-NAMEWS-FIRST-NAME の中身を連結しています。DELIMITED BY SPACE を使うことで、変数内の実際の文字(この場合は ‘山田’ と ‘太郎’)の末尾までを連結対象とし、後続の不要なスペースを除外しています。間に半角スペース ' 'DELIMITED BY SIZE で連結することで、姓名の間にスペースを入れています。

便利なオプション句

STRING 文には、さらに細かい制御を行うためのオプション句があります。

オプション句 説明
WITH POINTER ポインタ変数 受け取り項目内の連結を開始する位置(文字位置、1から始まる)を指定します。指定したポインタ変数は、連結された文字数分だけ自動的に加算されます。これにより、追記するような操作が可能です。ポインタ変数は事前に初期化(通常は1)しておく必要があります。
ON OVERFLOW 命令文-1 受け取り項目のサイズを超えて連結しようとした場合(オーバーフロー)に実行される処理を記述します。
NOT ON OVERFLOW 命令文-2 オーバーフローが発生しなかった場合に実行される処理を記述します。

WITH POINTER の使用例 (例2: 追記処理)


IDENTIFICATION DIVISION.
PROGRAM-ID. STRING-SAMPLE2.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BUFFER       PIC X(50) VALUE SPACES.
01 WS-POINTER      PIC 9(3) VALUE 1. *> 初期値は1
01 WS-ITEM1        PIC X(10) VALUE '商品A'.
01 WS-ITEM2        PIC 9(5) VALUE 1500.
01 WS-ITEM3        PIC X(8) VALUE '在庫あり'.
PROCEDURE DIVISION.
  *> 1回目の連結
  STRING WS-ITEM1 DELIMITED BY SPACE
         ','       DELIMITED BY SIZE
      INTO WS-BUFFER
      WITH POINTER WS-POINTER
  END-STRING.
  *> WS-BUFFER: '商品A      ,' (WS-ITEM1の後続スペースも含む)
  *> WS-POINTER: 4 (連結された文字数+1) ※処理系により挙動が異なる場合あり

  *> POINTERを調整して、ITEM1の後続スペースを除外した位置から始める例
  MOVE 1 TO WS-POINTER.
  STRING FUNCTION TRIM(WS-ITEM1) DELIMITED BY SIZE *> TRIM関数で後続スペース除去
         ','                     DELIMITED BY SIZE
      INTO WS-BUFFER
      WITH POINTER WS-POINTER
  END-STRING.
  *> WS-BUFFER: '商品A,'
  *> WS-POINTER: 5 (連結された文字数+1)

  *> 2回目の連結 (続けて)
  STRING WS-ITEM2 DELIMITED BY SIZE *> 数値項目はそのまま連結
         ','       DELIMITED BY SIZE
      INTO WS-BUFFER
      WITH POINTER WS-POINTER
  END-STRING.
  *> WS-BUFFER: '商品A,01500,'
  *> WS-POINTER: 11

  *> 3回目の連結 (続けて)
  STRING WS-ITEM3 DELIMITED BY SPACE
      INTO WS-BUFFER
      WITH POINTER WS-POINTER
  END-STRING.
  *> WS-BUFFER: '商品A,01500,在庫あり    '
  *> WS-POINTER: 19

  DISPLAY '連結結果: ' FUNCTION TRIM(WS-BUFFER). *> 結果: 商品A,01500,在庫あり

  STOP RUN.
        

⚠️ 注意: DELIMITED BY の指定によって、送り出し項目のどこまでが連結されるかが変わります。特に後続スペースの扱いには注意しましょう。また、WITH POINTER の値の更新ルールは処理系によって微妙に異なる可能性があるため、使用しているCOBOLコンパイラのドキュメントを確認することをお勧めします。最近のCOBOL (GnuCOBOLなど) では FUNCTION TRIM() を使うと、より簡単に前後のスペースを除去できます。

UNSTRING文:文字列を分解する

UNSTRING 文は、STRING 文とは逆に、一つの文字列を指定した区切り文字(デリミタ)で分解し、複数の変数に格納するための命令文です。CSVデータの解析や、固定フォーマットの電文の分解など、外部から受け取ったデータを処理する際によく利用されます。

基本的な構文は以下のようになります。


UNSTRING 分解元データ項目
    [DELIMITED BY [ALL] 区切り文字-1 [OR [ALL] 区切り文字-2] ...]
    INTO 受け取り項目-1 [DELIMITER IN 区切り文字格納項目-1] [COUNT IN 文字数格納項目-1]
         受け取り項目-2 [DELIMITER IN 区切り文字格納項目-2] [COUNT IN 文字数格納項目-2]
         ...
    [WITH POINTER ポインタ変数]
    [TALLYING IN 分解数格納項目]
    [ON OVERFLOW 命令文-1]
    [NOT ON OVERFLOW 命令文-2]
END-UNSTRING
      
ポイント💡
  • 分解元データ項目: 分解したい文字列が格納されている変数を指定します。
  • DELIMITED BY: 文字列を分割するための区切り文字(デリミタ)を指定します。複数の区切り文字を OR で繋げて指定できます。ALL を付けると、連続する区切り文字を1つとして扱います。
  • INTO: 分解された各部分文字列を格納する変数を順番に指定します。
  • END-UNSTRING: UNSTRING文の終わりを示します。

基本的な使い方 (例3: カンマ区切りデータの分解)

カンマ(,)で区切られたデータを分解してみましょう。


IDENTIFICATION DIVISION.
PROGRAM-ID. UNSTRING-SAMPLE1.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CSV-DATA     PIC X(50) VALUE '商品B,2500,在庫僅少'.
01 WS-ITEM-NAME    PIC X(10).
01 WS-PRICE        PIC 9(5).
01 WS-STOCK-STATUS PIC X(10).
PROCEDURE DIVISION.
  UNSTRING WS-CSV-DATA DELIMITED BY ','
      INTO WS-ITEM-NAME
           WS-PRICE
           WS-STOCK-STATUS
  END-UNSTRING.

  DISPLAY '商品名: ' WS-ITEM-NAME.    *> 結果: 商品名: 商品B
  DISPLAY '価格  : ' WS-PRICE.        *> 結果: 価格  : 02500
  DISPLAY '在庫  : ' WS-STOCK-STATUS. *> 結果: 在庫  : 在庫僅少

  STOP RUN.
      

WS-CSV-DATA の中身が DELIMITED BY ',' によってカンマで分解され、それぞれの部分が INTO 句で指定された変数に順番に格納されます。

便利なオプション句

UNSTRING 文にも、処理を細かく制御するためのオプション句があります。

オプション句 説明
DELIMITER IN 区切り文字格納項目 INTO で指定した変数の直前に、どの区切り文字で区切られたかを格納します。複数の区切り文字を指定した場合に便利です。
COUNT IN 文字数格納項目 INTO で指定した変数に格納された実際の文字数を格納します。PIC句のサイズではなく、格納された文字数です。
WITH POINTER ポインタ変数 分解を開始する分解元データ項目内の位置(文字位置、1から始まる)を指定します。指定したポインタ変数は、分解処理が進むにつれて自動的に加算されます。これにより、文字列の途中から分解を開始できます。ポインタ変数は事前に初期化しておく必要があります。
TALLYING IN 分解数格納項目 INTO 句で指定した受け取り項目のうち、実際にデータが格納された項目の数をカウントします。
ON OVERFLOW 命令文-1 分解元データ項目の最後まで処理する前に、INTO で指定したすべての受け取り項目にデータが格納されてしまった場合、またはポインタ変数の値が不正(1未満、または分解元データ長より大きい)になった場合に実行される処理を記述します。
NOT ON OVERFLOW 命令文-2 オーバーフローが発生しなかった場合に実行される処理を記述します。

オプション句の使用例 (例4: 詳細情報付き分解)


IDENTIFICATION DIVISION.
PROGRAM-ID. UNSTRING-SAMPLE2.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-INPUT-DATA   PIC X(80) VALUE 'ID:123/Name:ABC Corp./City:Tokyo'.
01 WS-FIELD1       PIC X(10).
01 WS-DELIM1       PIC X(1).
01 WS-COUNT1       PIC 9(3).
01 WS-FIELD2       PIC X(20).
01 WS-DELIM2       PIC X(1).
01 WS-COUNT2       PIC 9(3).
01 WS-FIELD3       PIC X(15).
01 WS-DELIM3       PIC X(1). *> 使われないが定義は必要
01 WS-COUNT3       PIC 9(3).
01 WS-POINTER      PIC 9(3) VALUE 1. *> 初期値1
01 WS-TALLY        PIC 9(3) VALUE 0. *> 初期値0
PROCEDURE DIVISION.
  UNSTRING WS-INPUT-DATA DELIMITED BY ':' OR '/'
      INTO WS-FIELD1 DELIMITER IN WS-DELIM1 COUNT IN WS-COUNT1
           WS-FIELD2 DELIMITER IN WS-DELIM2 COUNT IN WS-COUNT2
           WS-FIELD3 DELIMITER IN WS-DELIM3 COUNT IN WS-COUNT3
      WITH POINTER WS-POINTER
      TALLYING IN WS-TALLY
      ON OVERFLOW
          DISPLAY 'エラー: データ格納領域が不足、またはポインタ異常'
  END-UNSTRING.

  DISPLAY '分解数: ' WS-TALLY. *> 結果: 分解数: 003

  DISPLAY '項目1 : ' WS-FIELD1 ' (区切り文字:' WS-DELIM1 ', 長さ:' WS-COUNT1 ')'.
  *> 結果: 項目1 : ID         (区切り文字::, 長さ:002)
  DISPLAY '項目2 : ' WS-FIELD2 ' (区切り文字:' WS-DELIM2 ', 長さ:' WS-COUNT2 ')'.
  *> 結果: 項目2 : 123               (区切り文字:/, 長さ:003)
  DISPLAY '項目3 : ' WS-FIELD3 ' (区切り文字:' WS-DELIM3 ', 長さ:' WS-COUNT3 ')'.
  *> 結果: 項目3 : Name       (区切り文字::, 長さ:004)
  *> ※FIELD3には'Name'までしか入らない。以降は次のUNSTRINGや手動解析が必要。

  DISPLAY 'Pointer位置: ' WS-POINTER. *> 結果: Pointer位置: 016

  *> 続きを処理する場合 (Pointerを利用)
  MOVE SPACES TO WS-FIELD1 WS-FIELD2 WS-FIELD3.
  MOVE 0 TO WS-TALLY.
  UNSTRING WS-INPUT-DATA DELIMITED BY ':' OR '/'
      INTO WS-FIELD1 DELIMITER IN WS-DELIM1 COUNT IN WS-COUNT1 *> ABC Corp.
           WS-FIELD2 DELIMITER IN WS-DELIM2 COUNT IN WS-COUNT2 *> City
           WS-FIELD3 DELIMITER IN WS-DELIM3 COUNT IN WS-COUNT3 *> Tokyo
      WITH POINTER WS-POINTER *> 前回の続きから
      TALLYING IN WS-TALLY
  END-UNSTRING.

  DISPLAY '--- 続き ---'.
  DISPLAY '分解数: ' WS-TALLY. *> 結果: 003
  DISPLAY '項目1 : ' WS-FIELD1 ' (区切り文字:' WS-DELIM1 ', 長さ:' WS-COUNT1 ')'.
  *> 結果: 項目1 : ABC Corp.  (区切り文字:/, 長さ:009)
  DISPLAY '項目2 : ' WS-FIELD2 ' (区切り文字:' WS-DELIM2 ', 長さ:' WS-COUNT2 ')'.
  *> 結果: 項目2 : City              (区切り文字::, 長さ:004)
  DISPLAY '項目3 : ' WS-FIELD3 ' (区切り文字:' WS-DELIM3 ', 長さ:' WS-COUNT3 ')'.
  *> 結果: 項目3 : Tokyo          (区切り文字: , 長さ:005) ※終端は区切り文字なし

  STOP RUN.
        

⚠️ 注意:

  • DELIMITED BY で指定した区切り文字自体は、INTO 句の変数には格納されません(DELIMITER IN で取得可能)。
  • 連続する区切り文字(例: `,,`)は、デフォルトでは空の文字列として扱われます。DELIMITED BY ALL 区切り文字 と指定すると、連続する区切り文字をまとめて1つの区切りとして扱います。
  • INTO で指定する受け取り項目は、分解される可能性のある最大数を想定して十分に用意する必要があります。足りない場合、残りのデータは分解されません(OVERFLOWにはならない場合がある)。
  • WITH POINTERTALLYING IN で使用する変数は、UNSTRING 文を実行する前に適切に初期化(通常は POINTER=1, TALLYING=0)する必要があります。

まとめ

今回は、COBOLにおける文字列操作の基本となる STRING 文と UNSTRING 文について学びました。

  • STRING文: 複数の要素を組み合わせて一つの文字列を作る時に使う。名前の連結やメッセージ作成に便利。
  • UNSTRING文: 一つの文字列を区切り文字で分割して複数の要素にする時に使う。CSVデータや固定長データの解析に便利。

これらの命令文は、DELIMITED BY, WITH POINTER, TALLYING IN, ON OVERFLOW などのオプション句を組み合わせることで、非常に柔軟な文字列操作が可能です。

最初は少し複雑に感じるかもしれませんが、サンプルコードを実際に動かしてみたり、オプション句を変えて試してみたりすることで、徐々に理解が深まります。特に、データの連結・分解は実務で頻繁に出てくる処理なので、しっかりと使い方をマスターしておきましょう!🚀

これで Step 3「制御構文と演算処理」は完了です!次の Step 4 では、COBOLのもう一つの重要な機能である「ファイル処理」について学んでいきます。お楽しみに!🎉

参考情報

より詳細な情報や他の使い方については、以下の公式ドキュメントなどを参照してください。

  • IBM Enterprise COBOL for z/OS – STRING Statement: (具体的なURLは検索結果から見つけられませんでしたが、IBMの公式ドキュメントサイトで検索してみてください)
  • GnuCOBOL Programmer’s Guide – STRING Statement / UNSTRING Statement: https://gnucobol.sourceforge.io/doc/gnucobol.html (オンラインマニュアル内で検索してください)
  • COBOL入門サイトなどの解説記事 (検索結果 [1], [6], [9] など)