この記事では、COBOLプログラミングにおける強力な命令、COBOL UNSTRING文について、基本から応用まで解説していきます。
カンマ区切り(CSV)やスペース区切りといったデータを扱うとき、特定の区切り文字で文字列を分割して、それぞれの部品を別々の入れ物(項目)にキレイに格納したい場面って、結構ありますよね。
そんな時、UNSTRING文が役に立ちます。ちょっと構文がややこしそう?オプションが多くて迷う?大丈夫!この記事を読めば、そんな悩みもスッキリ解消するはずです。
この記事を読むと、こんなことができるようになりますよ。
- UNSTRING文が何をするための命令か理解できる
- UNSTRING文の基本的な書き方を覚えられる
- サンプルコードを通して、具体的な使い方をマスターできる
- 複数の区切り文字やオプションの使い分けがわかる
- UNSTRING文を使うときの注意点を知り、ミスを防げる
COBOLの「UNSTRING文」とは?文字列分割の基本を理解しよう
まず、UNSTRING文って何者?というところから始めましょう。
簡単に言うと、「一つの長い文字列を、指定したルール(区切り文字)に従って、いくつかの短い文字列に分解して、それぞれ別の場所にしまう」ためのCOBOL命令文です。
例えば、「山田太郎,30歳,東京都」みたいなカンマで区切られたデータがあったとします。これを「名前」「年齢」「住所」という別々の入れ物(変数や項目といいます)に入れたいとき、UNSTRING文の出番です。
「カンマ(,)で区切ってね!」とUNSTRING文に指示すれば、「山田太郎」「30歳」「東京都」と分けて、指定した入れ物に順番に入れてくれる、というわけです。
昔ながらの固定長のデータだけでなく、最近よく見るCSVファイルのように、データの長さがバラバラで区切り文字で分けられているデータを扱うときに、とっても重宝しますよ。
「UNSTRING文」の基本的な書き方
では、実際にUNSTRING文をどう書くのか、その骨組み(構文)を見ていきましょう。
基本形はこんな感じです。
UNSTRING 分割したい元のデータ項目 [ DELIMITED BY 区切り文字1 [OR 区切り文字2]... | ALL 区切り文字 ] INTO 格納先の項目1 [DELIMITER IN 区切り文字格納先1] [COUNT IN カウント格納先1] [格納先の項目2 [DELIMITER IN 区切り文字格納先2] [COUNT IN カウント格納先2]] ... [ WITH POINTER ポインター変数 ] [ TALLYING IN 格納した項目数を数える変数 ] [ ON OVERFLOW 命令実行後の処理 ] [ NOT ON OVERFLOW 命令実行後の処理 ] END-UNSTRING.
うーん、ちょっとオプションが多いですね!でも安心してください。全部を一度に覚える必要はありません。
まずは、「どのデータを(UNSTRING)」、「何で区切って(DELIMITED BY)」、「どこに入れるか(INTO)」の3つが基本だと押さえておけばOKです。
UNSTRING文の全体構造
まずは、一番シンプルなUNSTRING文の構造をつかみましょう。最低限、これだけあれば動きます。
UNSTRING 分割元データ DELIMITED BY 区切り文字 INTO 格納先データ1, 格納先データ2 ... END-UNSTRING.
図でイメージするとこんな感じでしょうか。
+----------------------+ +----------------+ +---------------------+ | UNSTRING | --> | 分割したいデータ | --> | DELIMITED BY | --> 区切り文字(',')など | (分割スタート!) | | (元の長い文字列) | | (これで区切ってね!) | +----------------------+ +----------------+ +---------------------+ | V +----------------------+ +----------------+ +----------------+ | INTO | --> | 格納先1 | --> | 格納先2 | --> ... | (ここに入れてね!) | | (分割後の部品1) | | (分割後の部品2) | +----------------------+ +----------------+ +----------------+
この基本形を頭に入れておくと、他のオプションも理解しやすくなりますよ。
主要な句(IDENTIFIER-1, DELIMITED BY, INTO)の役割
UNSTRING文を使いこなす上で、核となる3つの「句(く)」について、もう少し詳しく見ていきましょう。
- UNSTRINGの後 (IDENTIFIER-1に相当)
ここには、分割したいデータが入っている項目(変数名)を書きます。例えば、WS-INPUT-DATA
のような名前の項目です。 - DELIMITED BY
ここには、何を使って文字列を区切るかを指定します。カンマなら','
、スペースならSPACE
、タブ文字ならX'09'
のように書きます。複数の文字を区切り文字にしたい場合は、OR
でつなげたり、ALL
やANY
を使ったりもできます(これは後で詳しく!)。 - INTO
ここには、分割した文字列を格納する先の項目(変数名)を順番に書きます。INTO WS-ITEM-1, WS-ITEM-2, WS-ITEM-3
のように、カンマで区切って複数指定します。分割された順番に、指定した項目へ格納されていきます。
まずはこの3つの役割をしっかり覚えましょう!
「UNSTRING文」の使い方
お待たせしました!ここからは、実際にCOBOLのコードを使いながら、UNSTRING文の使い方を具体的に見ていきましょう。
基本的な使い方:1つの区切り文字で分割
まずは一番シンプルなパターン、カンマ(,)で区切られたデータを分割してみましょう。
ソースコード
IDENTIFICATION DIVISION. PROGRAM-ID. UNSTRING-BASIC. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-INPUT-DATA PIC X(30) VALUE 'Taro Yamada,30,Tokyo'. 01 WS-NAME PIC X(15). 01 WS-AGE PIC X(3). *> 文字列として受け取る 01 WS-ADDRESS PIC X(15). PROCEDURE DIVISION. DISPLAY '分割前データ: ' WS-INPUT-DATA. UNSTRING WS-INPUT-DATA DELIMITED BY ',' *> カンマで区切る INTO WS-NAME, WS-AGE, WS-ADDRESS *> この順番で格納 END-UNSTRING. DISPLAY '--- 分割後 ---'. DISPLAY '名前 : ' WS-NAME. DISPLAY '年齢 : ' WS-AGE. DISPLAY '住所 : ' WS-ADDRESS. STOP RUN.
ソースコードの表示結果
分割前データ: Taro Yamada,30,Tokyo --- 分割後 --- 名前 : Taro Yamada 年齢 : 30 住所 : Tokyo
どうでしょう?WS-INPUT-DATA
に入っていた文字列が、カンマを境にして WS-NAME
, WS-AGE
, WS-ADDRESS
にちゃんと分割・格納されましたね!
ポイントは、INTO句に書いた項目の順番通りにデータが入ることです。年齢(AGE)は数字ですが、まずは文字列(PIC X)として受け取るのが一般的です。後で数字に変換する必要があれば、別途処理を行います。
複数の区切り文字を使う方法 (DELIMITED BY ALL / ANY)
区切り文字が1種類だけとは限りませんよね。カンマもスペースも区切り文字として扱いたい、なんてこともあります。
そんなときは DELIMITED BY
の書き方を工夫します。
例えば、カンマかスラッシュ(/)で区切りたい場合:
DELIMITED BY ',' OR '/'
または、ANY
を使っても同じことができます。
DELIMITED BY ANY ',' '/'
ALL
という指定もあります。これは、連続する区切り文字を1つとして扱うときに便利です。例えば、'A,,B'
のようにカンマが連続しているデータを DELIMITED BY ALL ','
で分割すると、A
と B
に分割されます(間の空っぽの部分は無視されます)。普通の DELIMITED BY ','
だと、A
、空っぽ、B
の3つに分割される挙動になることが多いので、違いを理解しておくと良いでしょう。
ソースコード (ANY使用例)
IDENTIFICATION DIVISION. PROGRAM-ID. UNSTRING-MULTI-DELIM. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-INPUT-DATA PIC X(30) VALUE 'Apple/Banana,Orange Mango'. 01 WS-TEMP-DATA PIC X(30). 01 WS-FRUIT-1 PIC X(10). 01 WS-FRUIT-2 PIC X(10). 01 WS-FRUIT-3 PIC X(10). 01 WS-FRUIT-4 PIC X(10). PROCEDURE DIVISION. DISPLAY '分割前データ: ' WS-INPUT-DATA. MOVE WS-INPUT-DATA TO WS-TEMP-DATA. *> スラッシュで最初に分割 UNSTRING WS-TEMP-DATA DELIMITED BY '/' INTO WS-FRUIT-1, WS-TEMP-DATA END-UNSTRING. *> カンマで分割 UNSTRING WS-TEMP-DATA DELIMITED BY ',' INTO WS-FRUIT-2, WS-TEMP-DATA END-UNSTRING. *> スペースで分割 UNSTRING WS-TEMP-DATA DELIMITED BY SPACE INTO WS-FRUIT-3, WS-FRUIT-4 END-UNSTRING. DISPLAY '--- 分割後 ---'. DISPLAY '果物1 : ' WS-FRUIT-1. DISPLAY '果物2 : ' WS-FRUIT-2. DISPLAY '果物3 : ' WS-FRUIT-3. DISPLAY '果物4 : ' WS-FRUIT-4. STOP RUN.
ソースコードの表示結果
分割前データ: Apple/Banana,Orange Mango --- 分割後 --- 果物1 : Apple 果物2 : Banana 果物3 : Orange 果物4 : Mango
スラッシュ、カンマ、スペースのどれが現れても、そこで区切ってくれているのがわかりますね!
分割後の格納先を複数指定する (INTO句)
INTO
句には、分割したデータを入れたい項目を、入れたい順番にカンマで区切って指定します。
例えば、INTO WS-ITEM-A, WS-ITEM-B, WS-ITEM-C
と書けば、分割された1番目のデータが WS-ITEM-A
に、2番目が WS-ITEM-B
に、3番目が WS-ITEM-C
に入ります。
ここで気になるのが、「分割された数」と「INTOで指定した数」が違ったらどうなるの?という点ですよね。
- 分割された数 < INTOで指定した数 の場合
余ったINTOの項目には、何も格納されません(通常は初期値のままか、スペースなどで埋められます)。 - 分割された数 > INTOで指定した数 の場合
INTOで指定した項目数までしか格納されず、入りきらなかったデータは捨てられてしまいます。これは意図しないデータ欠損につながる可能性があるので注意が必要です。
格納先の項目は、分割されるデータの最大長を考慮して、十分な桁数(PIC)を確保しておくのが基本です。
*> 例:分割されるデータよりINTO句の項目が少ない IDENTIFICATION DIVISION. PROGRAM-ID. UNSTRING-INTO-FEW. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-INPUT PIC X(20) VALUE 'A,B,C,D,E'. 01 WS-OUT-1 PIC X(5). 01 WS-OUT-2 PIC X(5). PROCEDURE DIVISION. INITIALIZE WS-OUT-1 WS-OUT-2. *> 初期化しておく UNSTRING WS-INPUT DELIMITED BY ',' INTO WS-OUT-1, WS-OUT-2 *> 2つしか指定しない END-UNSTRING. DISPLAY 'OUT-1: ' WS-OUT-1. DISPLAY 'OUT-2: ' WS-OUT-2. *> C,D,Eは格納されない STOP RUN.
この例だと、WS-OUT-1
に'A'、WS-OUT-2
に'B'が入り、'C','D','E'はどこにも格納されません。
分割位置を制御する (WITH POINTER句)
WITH POINTER
句を使うと、UNSTRING文が文字列のどこから分割処理を開始するかを指定したり、どこまで処理が進んだかを知ったりできます。
これを使うには、まず作業領域(WORKING-STORAGE SECTION)に、ポインター用の数字項目(例えば WS-POINTER PIC 9(3)
のように)を用意します。
UNSTRING文を実行する前に、このポインター変数に開始したい位置(通常は1)を設定しておきます。
UNSTRING文が実行されると、指定した位置から分割が始まり、処理が進むにつれてポインター変数の値も増えていきます。そして、UNSTRING文が終わったとき、ポインター変数は「次に処理を開始するべき位置」を指しています。
これを利用すると、例えば、一度のUNSTRING文では分割しきれないほど長いデータを、ループ処理で少しずつ分割していく、といった応用が可能になります。
ソースコード
IDENTIFICATION DIVISION. PROGRAM-ID. UNSTRING-POINTER. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-INPUT-DATA PIC X(30) VALUE 'ITEM1,ITEM2,ITEM3,ITEM4'. 01 WS-ITEM PIC X(10). 01 WS-POINTER PIC 9(3). *> ポインター用変数 PROCEDURE DIVISION. MOVE 1 TO WS-POINTER. *> 開始位置を1に設定 DISPLAY '--- 1回目のUNSTRING ---'. UNSTRING WS-INPUT-DATA DELIMITED BY ',' INTO WS-ITEM WITH POINTER WS-POINTER *> ポインターを指定 END-UNSTRING. DISPLAY '取得ITEM: ' WS-ITEM. DISPLAY 'POINTER位置: ' WS-POINTER. *> 処理が進んだ位置が表示される DISPLAY '--- 2回目のUNSTRING ---'. UNSTRING WS-INPUT-DATA DELIMITED BY ',' INTO WS-ITEM WITH POINTER WS-POINTER *> 前回の続きから開始 END-UNSTRING. DISPLAY '取得ITEM: ' WS-ITEM. DISPLAY 'POINTER位置: ' WS-POINTER. STOP RUN.
ソースコードの表示結果
--- 1回目のUNSTRING --- 取得ITEM: ITEM1 POINTER位置: 007 *> ITEM1と,の次の位置 --- 2回目のUNSTRING --- 取得ITEM: ITEM2 POINTER位置: 013 *> ITEM2と,の次の位置
1回目のUNSTRINGが終わった後、WS-POINTER
が次の開始位置(7文字目)を指しているので、そのまま2回目のUNSTRINGを実行すると、ちゃんとITEM2
から処理が始まっているのがわかりますね。ポインター変数の初期化(最初に1を入れるなど)を忘れないのがコツです。
分割した項目数をカウントする (TALLYING IN句)
TALLYING IN
句を使うと、そのUNSTRING文で実際にいくつの項目がINTO句の変数に格納されたかを数えることができます。
これも、カウント結果を格納するための数字項目(例えば WS-COUNT PIC 9(3)
のように)を作業領域に用意して使います。
UNSTRING文が実行されると、分割して格納した項目の数が、このカウント用変数に自動的にセットされます。
分割されるデータの項目数が毎回変わるような場合に、後続の処理(例えば、取得した項目数だけループするなど)を制御するのに便利です。また、想定通りの数だけ分割できたかを確認するのにも使えますね。
ソースコード
IDENTIFICATION DIVISION. PROGRAM-ID. UNSTRING-TALLYING. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-INPUT-DATA PIC X(30) VALUE 'A,B,C'. 01 WS-ITEM-1 PIC X(5). 01 WS-ITEM-2 PIC X(5). 01 WS-ITEM-3 PIC X(5). 01 WS-ITEM-4 PIC X(5). *> 使われない格納先 01 WS-COUNT PIC 9(3). *> カウント用変数 PROCEDURE DIVISION. INITIALIZE WS-ITEM-1 WS-ITEM-2 WS-ITEM-3 WS-ITEM-4. MOVE 0 TO WS-COUNT. *> カウント変数を初期化 UNSTRING WS-INPUT-DATA DELIMITED BY ',' INTO WS-ITEM-1, WS-ITEM-2, WS-ITEM-3, WS-ITEM-4 TALLYING IN WS-COUNT *> 格納数をカウント END-UNSTRING. DISPLAY 'ITEM-1: ' WS-ITEM-1. DISPLAY 'ITEM-2: ' WS-ITEM-2. DISPLAY 'ITEM-3: ' WS-ITEM-3. DISPLAY 'ITEM-4: ' WS-ITEM-4. *> ここは空のはず DISPLAY '格納された項目数: ' WS-COUNT. *> 3 が表示されるはず STOP RUN.
ソースコードの表示結果
ITEM-1: A ITEM-2: B ITEM-3: C ITEM-4: 格納された項目数: 003
ちゃんと、実際に格納された項目数「3」が WS-COUNT
に入っていますね。TALLYINGで使う変数も、実行前に0などで初期化しておくのがお作法です。
COBOL「UNSTRING文」を使う上での注意点
便利なUNSTRING文ですが、いくつか気をつけておきたいポイントがあります。思わぬエラーやデータの食い違いを防ぐために、頭の片隅に入れておいてくださいね。
- 格納先項目の桁あふれ (OVERFLOW)
分割されたデータが、INTO
句で指定した格納先項目(PIC X(5)など)の桁数より長かった場合、基本的にはデータが途中でちょん切れてしまいます。全部ちゃんと入るように、格納先の桁数は余裕をもって定義しましょう。ON OVERFLOW
句を指定しておくと、桁あふれが発生した場合に特別な処理(エラーメッセージを出すなど)を実行させることも可能です。 - 区切り文字が見つからない
DELIMITED BY
で指定した区切り文字が、分割対象の文字列中に一つも見つからなかった場合、分割は行われず、INTO
句の最初の項目に元の文字列全体が(格納先の桁数分だけ)コピーされる、といった挙動になることが多いです。意図しない結果になる可能性があるので注意しましょう。 - POINTER変数・TALLYING変数の初期化
WITH POINTER
句やTALLYING IN
句を使う場合、UNSTRING文を実行する前に、必ずそれぞれの変数を適切な値(POINTERなら通常1、TALLYINGなら0)に初期化(MOVE命令などで値を設定)しましょう。初期化を忘れると、前の処理で使われた値が残っていて、予期せぬ動きをする原因になります。 - 項目の属性
UNSTRING文は基本的に文字列を扱います。分割元のデータも、格納先の項目も、通常は文字タイプ(PIC X)で定義します。数字として扱いたい場合でも、一度PIC Xで受け取ってから、後で数字項目(PIC 9)に転記・変換するのが安全です。 - END-UNSTRINGの書き忘れ
UNSTRING文の終わりには、END-UNSTRING
を書くのを忘れずに。書き忘れるとコンパイルエラーになったり、意図しない範囲までUNSTRING文の影響が及んだりすることがあります。
これらの点に気をつければ、UNSTRING文はあなたの強力な味方になってくれますよ!
【まとめ】COBOLの「UNSTRING文」をマスターして効率的なデータ処理を
今回は、COBOLのUNSTRING文について、基本的な考え方から具体的な使い方、そして注意点まで、サンプルコードを交えながら解説してきました。
UNSTRING文は、カンマ区切りやスペース区切りなどのデータを扱う際に、文字列を効率よく分割・整理するための非常に便利な命令です。
DELIMITED BY
で区切り方を指定し、INTO
で格納先を指定するのが基本。さらに WITH POINTER
や TALLYING IN
などのオプションを使えば、より複雑なデータ処理にも対応できます。
最初は少しとっつきにくい部分もあるかもしれませんが、この記事で紹介したサンプルコードなどを参考に、実際に手を動かして試してみるのが一番の近道です。
UNSTRING文を使いこなせるようになれば、COBOLでのデータ加工やファイル処理の幅がぐっと広がります。ぜひ、あなたのCOBOLスキルの一つとして、自信を持って活用していってくださいね!応援しています!
【関連記事】
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。