COBOLプログラミングで避けては通れない「COBOL STRING文」、しっかり使いこなせていますか?
「複数の項目をくっつけたいんだけど、どう書くの?」「特定の文字までを連結したい…」「なんかエラーになっちゃう!」
そんな悩みを抱えているCOBOLビギナーの皆さん、お待たせしました!
この記事では、COBOLのSTRING文について、基本的な書き方から、ちょっと便利な使い方、そして「あちゃー!」となりがちな注意点まで、サンプルコードをたっぷり使って解説していきます。
これを読めば、STRING文のモヤモヤが晴れて、自信を持ってコードを書けるようになりますよ!さあ、一緒に文字列操作マスターを目指しましょう!
この記事で学べること
- STRING文が何をする命令なのかがわかる
- STRING文の基本的な書き方のルールが身につく
- DELIMITED BY句の使い分けができるようになる
- POINTER句を使った応用的な連結方法がわかる
- STRING文でよくある失敗とその対策がわかる
COBOLの文字列操作に必須!「STRING文」とは?
まずは基本から! COBOLのSTRING文というのは、一言でいうと「複数の文字列(データ項目や文字そのもの)を、ルールに従って一つにくっつける(連結する)」ための命令です。
例えば、こんな時に出番がやってきます。
- 社員名簿で、「姓」と「名」をくっつけて「氏名」を作りたいとき。
- 住所データで、「都道府県」「市区町村」「番地」を連結して、宛名ラベル用の住所を作りたいとき。
- 画面に表示するメッセージで、固定の文言と変数の値を組み合わせて表示したいとき。
データを加工したり、見やすく整形したりする場面で、とてもよく使われる命令なんです。
地味に見えるかもしれませんが、これが使えるとデータの表現力がグッと上がりますよ。
「STRING文」の基本的な書き方(構文)
では、STRING文がどういう形をしているのか、その基本構造を見ていきましょう。
命令の書き方にはルールがあります。まずは骨格をつかむことが肝心です。
STRING 送り出し項目1 DELIMITED BY 区切り指定1 送り出し項目2 DELIMITED BY 区切り指定2 ... INTO 結果を格納する項目 [ WITH POINTER ポインタ変数 ] [ ON OVERFLOW エラー処理 ] [ NOT ON OVERFLOW 正常終了処理 ] END-STRING.
うーん、なんだか呪文みたいですね(笑)でも大丈夫、分解すれば難しくありません。
- STRING ... END-STRING
まず、命令の始まりと終わりを示す合言葉です。 - 送り出し項目 + DELIMITED BY 区切り指定
「どのデータ(送り出し項目)を」「どこまでくっつけるか(DELIMITED BY)」を指定するペアです。これを連結したい数だけ繰り返します。 - INTO 結果を格納する項目
連結した結果を、どの変数に入れるかを指定します。ここは必須です! - [ WITH POINTER ポインタ変数 ]
連結結果を入れる変数の、どこから書き始めるかを細かく指定したいときに使います。([ ]で囲まれているのは、省略可能なオプションという意味です) - [ ON OVERFLOW エラー処理 ]
連結結果が、入れる変数(INTOで指定した項目)のサイズを超えちゃった(あふれちゃった)場合に、どういう処理をするか書く場所です。 - [ NOT ON OVERFLOW 正常終了処理 ]
あふれなかった場合に、どういう処理をするか書く場所です。
基本は「何を」「どこまで」くっつけて、「どこに入れるか」を指定する、という流れを覚えておきましょう。
文字列を指定する:送出し項目
「送り出し項目」というのは、連結したい元ネタのデータのことです。
これは、プログラムの中で定義した変数(データ項目)でもいいですし、「ABC」や「様」のような固定の文字(文字リテラル)を直接書くこともできます。
例えば、「山田」という名字が入った変数 `WS-LAST-NAME` と、「太郎」という名前が入った変数 `WS-FIRST-NAME`、そして固定の文字「様」を連結したい場合は、こんな感じで指定します。
WS-LAST-NAME WS-FIRST-NAME '様'
これらを STRING文の中で順番に書いていくイメージですね。
区切り文字を指定する:DELIMITED BY句
ここがSTRING文のキモの一つ、`DELIMITED BY`句です。
これは、「送り出し項目」のどこまでを連結対象とするかを決める、とても大事な指示になります。
主に3つの指定方法があります。
- DELIMITED BY SPACE
送り出し項目の先頭から見ていって、最初のスペース(空白)が見つかるまでを連結対象にします。もしスペースがなければ、項目全体が対象になります。名前に含まれるスペースまでで区切りたい時などに使えますね。 - DELIMITED BY SIZE
送り出し項目が定義されている長さ分、全部を連結対象にします。途中にスペースがあろうが関係ありません。「この変数の値は丸ごと全部くっつけたい!」という時に使います。これが一番シンプルでよく使われるかもしれません。 - DELIMITED BY '特定の文字'
送り出し項目の先頭から見ていって、指定した特定の文字(例:',' カンマや '/' スラッシュなど)が見つかるまでを連結対象にします。その特定の文字自体は連結されません。区切り文字でデータが区切られている場合に便利です。
どの `DELIMITED BY` を使うかで、連結される文字列が全然変わってくるので、目的に合わせてしっかり選ぶ必要がありますよ。
【DELIMITED BY のイメージ】 変数A (PIC X(10)) の中身: "ABC DEF " DELIMITED BY SPACE の場合 → "ABC" が連結される DELIMITED BY SIZE の場合 → "ABC DEF " が連結される DELIMITED BY 'D' の場合 → "ABC " が連結される (Dの手前まで)
結果を格納する:INTO句
連結した文字列を最終的にどこに入れるか、そのゴール地点を指定するのが`INTO`句です。
`INTO`の後には、結果を格納するための変数(データ項目)を書きます。
INTO WS-RESULT-AREA
ここで気をつけたいのが、格納先の変数のサイズ(長さ)です。
連結した結果の文字列が、この格納先のサイズを超えてしまうと、「領域あふれ(オーバーフロー)」というエラーの原因になります。
例えば、10文字しか入らない箱(変数)に、15文字のデータを無理やり詰め込もうとするようなものです。当然、入りきらないですよね?
なので、`INTO`で指定する変数は、連結後の最大文字数をよーく考えて、十分な大きさを確保しておくようにしましょう!
「STRING文」の使い方:サンプルコードで実践!
お待たせしました!理屈だけじゃなく、実際にコードを動かしてSTRING文の動きを見ていきましょう。
いくつかパターンを用意したので、皆さんの環境で試してみてくださいね。コピーして、コンパイルして、実行! これが一番の近道です。
基本的な文字列連結(DELIMITED BY SPACE/SIZE)
まずは、名字と名前を連結するシンプルな例です。`DELIMITED BY SPACE`と`DELIMITED BY SIZE`の違いに注目してみてください。
IDENTIFICATION DIVISION. PROGRAM-ID. STRING-SAMPLE1. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-LAST-NAME PIC X(10) VALUE 'YAMADA '. *> 後ろにスペースあり 01 WS-FIRST-NAME PIC X(10) VALUE 'TARO '. *> 後ろにスペースあり 01 WS-FULL-NAME1 PIC X(20) VALUE SPACES. 01 WS-FULL-NAME2 PIC X(20) VALUE SPACES. PROCEDURE DIVISION. *> DELIMITED BY SPACE を使った場合 STRING WS-LAST-NAME DELIMITED BY SPACE WS-FIRST-NAME DELIMITED BY SPACE INTO WS-FULL-NAME1 END-STRING. DISPLAY 'DELIMITED BY SPACE 結果: [' WS-FULL-NAME1 ']'. *> DELIMITED BY SIZE を使った場合 STRING WS-LAST-NAME DELIMITED BY SIZE WS-FIRST-NAME DELIMITED BY SIZE INTO WS-FULL-NAME2 END-STRING. DISPLAY 'DELIMITED BY SIZE 結果: [' WS-FULL-NAME2 ']'. STOP RUN.
実行結果
DELIMITED BY SPACE 結果: [YAMADATARO ] DELIMITED BY SIZE 結果: [YAMADA TARO ]
どうでしょう? `DELIMITED BY SPACE` だと、それぞれの変数の最初のスペースの手前までが連結されるので、「YAMADA」と「TARO」がくっついて表示されます。
一方、`DELIMITED BY SIZE` だと、変数の定義された長さ分(10文字)がそのまま連結されるので、変数内の後ろのスペースも一緒にくっついてきます。
名字と名前の間にスペースを入れたい場合は、`DELIMITED BY SPACE` を使って、間に `' '` (スペース一文字) を挟んで連結する、といった工夫ができますね。
特定の文字で区切って連結(DELIMITED BY '文字')
次は、データの中に区切り文字がある場合に、それを利用して連結する例です。
ここでは、カンマ(,)で区切られた商品データから、商品コードと商品名を取り出して連結してみましょう。
IDENTIFICATION DIVISION. PROGRAM-ID. STRING-SAMPLE2. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-ITEM-DATA PIC X(30) VALUE 'A001,APPLE PIE,300'. 01 WS-ITEM-CODE PIC X(04). 01 WS-ITEM-NAME PIC X(15). 01 WS-DISPLAY-ITEM PIC X(25) VALUE SPACES. 01 WS-POINTER PIC 9(02) VALUE 1. *> 文字列走査用ポインタ PROCEDURE DIVISION. *> まずはUNSTRINGで分解(STRING文の仲間みたいな命令です) UNSTRING WS-ITEM-DATA DELIMITED BY ',' INTO WS-ITEM-CODE WS-ITEM-NAME *> 価格部分は無視 END-UNSTRING. *> 分解したコードと名前をSTRINGで連結 STRING WS-ITEM-CODE DELIMITED BY SIZE *> コードは全部 ' : ' DELIMITED BY SIZE *> 区切り文字を追加 WS-ITEM-NAME DELIMITED BY SIZE *> 名前も全部 INTO WS-DISPLAY-ITEM END-STRING. DISPLAY '商品情報: [' WS-DISPLAY-ITEM ']'. STOP RUN.
実行結果
商品情報: [A001 : APPLE PIE ]
この例では、まず`UNSTRING`という命令(STRINGの逆バージョンみたいなもの)でカンマ区切りのデータを分解し、その後`STRING`で必要な情報(商品コードと商品名)と、見やすくするための区切り文字 ` : ` を連結しています。
`DELIMITED BY '文字'` は、主に`UNSTRING`とセットで使われることが多いですが、特定のマーカーまでを連結したい場合にも応用できます。
連結位置を調整する:POINTER句の使い方
最後に、ちょっと応用編!`POINTER`句を使ってみましょう。
これは、`INTO`で指定した格納先変数の、何文字目から連結を開始するかを指定できるオプションです。
例えば、既にある文字列の後ろに、さらに別の文字列を追記したい、なんて時に便利です。
IDENTIFICATION DIVISION. PROGRAM-ID. STRING-SAMPLE3. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-MESSAGE PIC X(30) VALUE 'HELLO '. *> 先頭に初期値 01 WS-NAME PIC X(10) VALUE 'WORLD'. 01 WS-POINTER PIC 9(02) VALUE 7. *> 初期値'HELLO 'の次の位置(7文字目) PROCEDURE DIVISION. *> WS-MESSAGEの7文字目から連結を開始する STRING WS-NAME DELIMITED BY SPACE INTO WS-MESSAGE WITH POINTER WS-POINTER *> ポインタ変数を指定 END-STRING. DISPLAY '連結後メッセージ: [' WS-MESSAGE ']'. DISPLAY '処理後のポインタ位置: ' WS-POINTER. STOP RUN.
実行結果
連結後メッセージ: [HELLO WORLD ] 処理後のポインタ位置: 12
`POINTER`句で使う変数(ここでは`WS-POINTER`)には、連結を開始したい位置(先頭は1)をあらかじめ入れておきます。
この例では、`WS-MESSAGE`には最初 'HELLO ' (6文字) が入っているので、7文字目から連結を開始するように `WS-POINTER` に 7 を設定しました。
STRING文が実行されると、指定された7文字目から `WS-NAME` ('WORLD') が連結されます。
そして、地味に便利なのが、STRING文が終わった後、`WS-POINTER`の値が自動的に更新されること! この例では、'WORLD' (5文字) を連結したので、ポインタの位置は 7 + 5 = 12 になっています。これにより、続けて追記していくような処理も簡単に書けるわけです。
POINTER句を使うときは、必ずポインタ変数を事前に適切な値に初期化しておくのを忘れないでくださいね!初期化しないと、どこから連結が始まるか分からなくなってしまいます。
要注意!COBOLの「STRING文」で気をつけるべき点
STRING文は便利ですが、いくつか注意しないと思わぬ落とし穴にはまることも…!
ここでは、特に初心者の人がつまずきやすいポイントを2つ紹介します。これを読んで、しっかり対策しておきましょう。
格納先の領域あふれ(Overflow)と対策
先ほど `INTO`句のところでも触れましたが、これが一番よくある失敗かもしれません。
連結した結果の文字列が、`INTO`で指定した格納先変数のサイズを超えてしまう「領域あふれ(オーバーフロー)」です。
【領域あふれのイメージ】 格納先変数 (PIC X(5)) [ ] 連結する文字列 "ABCDEF" (6文字) → 入りきらない! あふれた "F" はどこへ…?
あふれた文字は、格納先の変数のメモリ領域を超えて、隣にある別の変数の領域を壊してしまう可能性があります。これが、予期せぬバグやプログラムの異常終了の原因になるんです。怖いですね!
対策としては、まず第一に格納先の変数は、連結後の最大文字数を考慮して十分に大きなサイズで定義しておくこと。これが基本です。
それでも、万が一あふれてしまった場合に備えて、`ON OVERFLOW`句を使うことを強くおすすめします。
STRING ... INTO WS-SMALL-AREA ON OVERFLOW DISPLAY 'エラー:格納領域があふれました!' *> ここにエラー処理を書く (例:処理中断フラグを立てるなど) END-STRING.
`ON OVERFLOW`句の中に処理を書いておけば、もし領域あふれが発生した場合に、その処理が実行されます。エラーメッセージを表示したり、処理を安全に停止させたりできるわけです。
逆に、正常に終了した場合だけ何かしたいなら `NOT ON OVERFLOW`句が使えます。安全なプログラムを作るためには、`ON OVERFLOW`でのチェックは習慣づけましょう!
もう一つ、見落としがちなのが、格納先変数やPOINTER句で使うポインタ変数の初期化です。
STRING文は、格納先の変数を自動で初期化(スペースで埋めたり)してくれません。
もし、格納先の変数に以前使ったゴミデータが残っていたり、ポインタ変数が想定外の値になっていたりすると、連結結果がおかしくなってしまいます。
STRING文を使う直前には、
- 格納先の変数(`INTO`で指定する項目)は、`MOVE SPACES TO ...` などで初期化する。
- `POINTER`句を使う場合は、ポインタ変数に正しい開始位置を設定する。
この2点をしっかり守るように心がけましょう!
【まとめ】COBOLの「STRING文」を使いこなそう!
さて、COBOLのSTRING文について、基本的な書き方から応用、注意点まで見てきました。
最後に、今回のポイントをまとめておきましょう。
- STRING文は、複数の文字列を連結するための命令。
- `DELIMITED BY`句(SPACE, SIZE, '文字')で、どこまで連結するかを指定するのが大事。
- `INTO`句で結果を入れる変数を指定。サイズ不足(オーバーフロー)に注意!
- `POINTER`句を使えば、連結開始位置を調整できる。
- 格納先変数やポインタ変数の初期化を忘れずに!
- 万が一の領域あふれ対策に `ON OVERFLOW`句を活用しよう。
最初はちょっと複雑に感じるかもしれませんが、サンプルコードを実際に動かしてみたり、自分で少し改造してみたりすると、すぐに慣れてくるはずです。
STRING文をマスターすれば、COBOLでのデータ加工や表示処理がもっと楽に、もっと柔軟になりますよ。
恐れずにどんどん使って、自分のものにしていきましょう!
この記事が、皆さんのCOBOLライフの一助となれば、とても嬉しいです!
【関連記事】
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。