COBOLで最も頻繁に使う命令文のひとつが MOVE です。「データを転送する」という単純な命令ですが、転送元と転送先のデータ型・桁数が異なるときの挙動を正確に理解していないと、切り捨て・ゼロ埋め・符号消失といったバグを生み出してしまいます。
本記事では基本構文から、数値型・文字型間の型変換ルール、フィギュアリティブコンスタント、MOVE CORRESPONDING、数値編集項目への転送、よくある落とし穴まで、MOVE文のすべてを解説します。
- MOVE文の基本構文と複数ターゲットへの一括転送
- 数値→数値・文字→文字・数値→文字・文字→数値の型変換ルール
- ZERO・SPACE・HIGH-VALUE・ALL など特殊フィギュアリティブの使い方
- MOVE CORRESPONDING(CORR)で同名フィールドを一括転送する方法
- グループ項目(01レベル)へのMOVEの注意点
- 数値編集項目(PIC ZZZ,ZZ9 等)への転送で帳票形式に整形する方法
- 符号消失・桁欠け・上書き漏れなどよくある落とし穴
MOVE文の基本構文
*> 1対1の転送
MOVE 転送元 TO 転送先
*> 1対多の転送(複数ターゲットに同時代入)
MOVE 転送元 TO 転送先-1 転送先-2 転送先-3
*> リテラル値の転送
MOVE "HELLO" TO WS-MSG
MOVE 12345 TO WS-NUM
MOVE ZERO TO WS-COUNTER *> フィギュアリティブ
01 WS-NAME-SRC PIC X(20) VALUE "TANAKA TARO".
01 WS-NAME-DEST1 PIC X(20).
01 WS-NAME-DEST2 PIC X(20).
01 WS-AMT-SRC PIC 9(7) VALUE 1500000.
01 WS-AMT-DEST PIC 9(7).
*> 文字列の転送
MOVE WS-NAME-SRC TO WS-NAME-DEST1 WS-NAME-DEST2
*> 数値の転送
MOVE WS-AMT-SRC TO WS-AMT-DEST
*> → WS-NAME-DEST1 = WS-NAME-DEST2 = "TANAKA TARO"
*> → WS-AMT-DEST = 1500000
データ型ごとの転送ルール
MOVEの動作はデータ型の組み合わせによって変わります。各パターンのルールを正確に把握しておくことが重要です。
文字型(X)→ 文字型(X):左寄せ・スペース埋め
文字型から文字型への転送は左寄せで行われます。転送先が長い場合は右側がスペースで埋まり、転送先が短い場合は右側が切り捨てられます。
01 WS-SRC PIC X(10) VALUE "ABCDE".
01 WS-L15 PIC X(15). *> 転送先が長い
01 WS-L03 PIC X(3). *> 転送先が短い
MOVE WS-SRC TO WS-L15
*> WS-L15 = "ABCDE " (右5文字がスペース)
MOVE WS-SRC TO WS-L03
*> WS-L03 = "ABC" (右7文字が切り捨て)
数値型(9)→ 数値型(9):右寄せ・ゼロ埋め
数値型から数値型への転送は右寄せです。転送先が大きい場合は左側がゼロで埋まり、転送先が小さい場合は上位桁が切り捨てられます(SIZE ERRORは発生しない)。
01 WS-NUM5 PIC 9(5) VALUE 12345.
01 WS-NUM8 PIC 9(8). *> 転送先が大きい
01 WS-NUM3 PIC 9(3). *> 転送先が小さい
MOVE WS-NUM5 TO WS-NUM8
*> WS-NUM8 = 00012345 (左3桁がゼロ埋め)
MOVE WS-NUM5 TO WS-NUM3
*> WS-NUM3 = 345 (上位2桁 "12" が切り捨て)!
COMPUTE文の
ON SIZE ERROR と異なり、MOVE文では転送先の桁数を超えても例外は発生しません。上位桁が静かに切り捨てられるだけです。桁数が足りるか必ず確認してから MOVE してください。小数を含む数値型の転送
01 WS-SRC PIC 9(5)V99 VALUE 12345.67.
01 WS-DEST1 PIC 9(7)V99. *> 整数部が広い
01 WS-DEST2 PIC 9(3)V9. *> 小数部が狭い
01 WS-DEST3 PIC 9(3). *> 小数部なし
MOVE WS-SRC TO WS-DEST1
*> WS-DEST1 = 0012345.67 (整数部左にゼロ埋め)
MOVE WS-SRC TO WS-DEST2
*> WS-DEST2 = 345.6 (整数部上位切り捨て・小数部切り捨て)
MOVE WS-SRC TO WS-DEST3
*> WS-DEST3 = 345 (小数部切り捨て・整数上位切り捨て)
数値型 → 文字型:ゾーン変換
数値をそのまま文字型に転送すると、ゾーン形式の内部表現が文字列として格納されます。表示用の変換には数値編集項目(後述)を使うのが正しい方法です。
01 WS-NUM PIC 9(5) VALUE 12345.
01 WS-CHAR PIC X(5).
01 WS-EDIT PIC Z(4)9. *> 数値編集項目
*> NG: 数値→文字型直接転送(実装依存・意図しない結果になることがある)
MOVE WS-NUM TO WS-CHAR
*> OK: 数値編集項目に転送して文字として扱う
MOVE WS-NUM TO WS-EDIT
*> WS-EDIT = " 2345" (先頭のゼロをスペースに変換)
フィギュアリティブコンスタント
COBOLには特殊な意味を持つ予約語「フィギュアリティブコンスタント」があります。初期化・クリア処理でよく使います。
| フィギュアリティブ | 値 | 主な用途 |
|---|---|---|
ZERO / ZEROS / ZEROES |
0(数値)またはスペース(文字) | 数値カウンタ・金額の初期化 |
SPACE / SPACES |
半角スペース(X’40’) | 文字項目のクリア |
HIGH-VALUE / HIGH-VALUES |
X’FF’(最大バイト値) | 検索終端マーク・最大値判定 |
LOW-VALUE / LOW-VALUES |
X’00’(最小バイト値) | バイナリデータの初期化 |
ALL "文字" |
指定文字で埋め尽くす | 区切り線・マスク文字 |
NULL / NULLS |
X’00’(ポインタ) | ポインタ変数の初期化 |
01 WS-COUNTER PIC 9(5).
01 WS-AMOUNT PIC S9(9)V99 COMP-3.
01 WS-NAME PIC X(20).
01 WS-FLAG PIC X(1).
01 WS-SEPARATOR PIC X(40).
*> 数値項目をゼロ初期化
MOVE ZERO TO WS-COUNTER
MOVE ZEROS TO WS-AMOUNT
*> 文字項目をスペースクリア
MOVE SPACES TO WS-NAME
*> HIGH-VALUE を検索終端マークとして使う
MOVE HIGH-VALUE TO WS-FLAG
*> ALL で区切り線を生成
MOVE ALL "-" TO WS-SEPARATOR
*> → WS-SEPARATOR = "----------------------------------------"
MOVE ZERO TO WS-NUM(数値)→ 0 が格納されます。MOVE ZERO TO WS-CHAR(文字)→ スペースが格納されます(文字の “0” ではありません)。MOVE SPACE TO WS-NUM(数値)→ 0 が格納されます(数値のゼロとして扱われる)。フィギュアリティブはデータ型に合わせて自動解釈されます。
MOVE CORRESPONDING(CORR):同名フィールドの一括転送
MOVE CORRESPONDING(略して MOVE CORR)は、転送元グループ項目と転送先グループ項目の中に同じ名前の従属項目がある場合、それらをまとめて一括転送します。
01 WS-INPUT.
05 WS-NAME PIC X(20).
05 WS-BIRTHDATE PIC 9(8).
05 WS-DEPT-CODE PIC X(4).
05 WS-INPUT-FLG PIC X(1). *> 転送先にはない
01 WS-RECORD.
05 WS-NAME PIC X(20).
05 WS-BIRTHDATE PIC 9(8).
05 WS-DEPT-CODE PIC X(4).
05 WS-SEQ-NO PIC 9(6). *> 転送元にはない
*> 同名フィールド(NAME・BIRTHDATE・DEPT-CODE)を一括転送
MOVE CORRESPONDING WS-INPUT TO WS-RECORD
*> = MOVE WS-NAME OF WS-INPUT TO WS-NAME OF WS-RECORD
*> MOVE WS-BIRTHDATE OF WS-INPUT TO WS-BIRTHDATE OF WS-RECORD
*> MOVE WS-DEPT-CODE OF WS-INPUT TO WS-DEPT-CODE OF WS-RECORD
*> WS-INPUT-FLG と WS-SEQ-NO は対象外(片方にしかない)
*> ---- MOVE CORR なしの場合(9行必要)----
MOVE WS-NAME OF WS-INPUT TO WS-NAME OF WS-RECORD
MOVE WS-BIRTHDATE OF WS-INPUT TO WS-BIRTHDATE OF WS-RECORD
MOVE WS-DEPT-CODE OF WS-INPUT TO WS-DEPT-CODE OF WS-RECORD
*> ---- MOVE CORR ありの場合(1行)----
MOVE CORRESPONDING WS-INPUT TO WS-RECORD
① 名前が同じでもデータ型・桁数が違うフィールドも転送されます(型変換は通常のMOVEと同じルールで行われます)。
② 名前の一致はグループ項目内の直接の従属項目のみが対象です(さらに下の階層は再帰的には探しません)。
③ フィールド数が多い構造での使用は、どのフィールドが転送されるか追いにくくなるため、保守性に注意してください。
グループ項目へのMOVE:バイト単位コピー
グループ項目(01レベルや05レベルなど、従属項目を持つ項目)に対して MOVE を行うと、型変換なしのバイト単位コピーが行われます。転送先の型は一切考慮されません。
01 WS-REC-A.
05 WS-CODE-A PIC X(4) VALUE "2024".
05 WS-AMOUNT-A PIC 9(6) VALUE 123456.
01 WS-REC-B.
05 WS-CODE-B PIC X(4).
05 WS-AMOUNT-B PIC 9(6).
*> グループ項目同士のMOVE → バイト単位コピー
MOVE WS-REC-A TO WS-REC-B
*> WS-CODE-B = "2024", WS-AMOUNT-B = 123456
*> 警告: 構造が異なるグループ間のMOVEは意図しない結果になりやすい
01 WS-REC-C.
05 WS-C-DATE PIC 9(8). *> 8桁数値
05 WS-C-FLAG PIC X(2). *> 2桁文字
MOVE WS-REC-A TO WS-REC-C *> A(10バイト)→C(10バイト)をバイトコピー
*> WS-C-DATE の内部表現がWS-CODE-Aの文字コードになる → 注意
数値編集項目への MOVE:帳票形式に整形する
COBOLの数値編集項目(Edited Numeric)は、数値を表示用に整形するための専用のPICTURE句です。MOVEで代入すると自動的に整形されます。
| PIC句 | 編集文字 | 値: 1234567 | 意味 |
|---|---|---|---|
PIC 9(7) |
なし(非編集) | 1234567 |
そのまま |
PIC Z(6)9 |
Z:先頭ゼロ→スペース | 234567 |
先頭ゼロ抑制 |
PIC ZZZ,ZZ9 |
Z+カンマ | 1,234,567 |
3桁カンマ区切り |
PIC +ZZZ,ZZ9 |
符号付き | +1,234,567 |
符号を表示 |
PIC -ZZZ,ZZ9 |
マイナス符号 | 1,234,567 |
負のとき – を表示 |
PIC ZZZ,ZZ9.99 |
小数点付き | 1,234,567.00 |
小数点表示 |
01 WS-AMOUNT PIC S9(7)V99 COMP-3 VALUE 1234567.89.
01 WS-DISP-BASIC PIC 9(7).
01 WS-DISP-Z PIC Z(6)9.
01 WS-DISP-COMMA PIC ZZZ,ZZZ,ZZ9.
01 WS-DISP-SIGN PIC +ZZZ,ZZZ,ZZ9.
01 WS-DISP-DECIMAL PIC ZZZ,ZZZ,ZZ9.99.
MOVE WS-AMOUNT TO WS-DISP-BASIC
DISPLAY WS-DISP-BASIC *> "1234567"
MOVE WS-AMOUNT TO WS-DISP-Z
DISPLAY WS-DISP-Z *> "1234567" (先頭ゼロ → スペース変換)
MOVE WS-AMOUNT TO WS-DISP-COMMA
DISPLAY WS-DISP-COMMA *> " 1,234,567"
MOVE WS-AMOUNT TO WS-DISP-DECIMAL
DISPLAY WS-DISP-DECIMAL *> "1,234,567.89"
数値編集項目(PIC ZZZ,ZZ9 等)から通常の数値項目(PIC 9(n))へ逆方向にMOVEすることはできません(コンパイルエラー)。帳票出力用の一方向変換として使います。計算には必ず元の非編集数値項目を使ってください。
よくある落とし穴と対策
落とし穴1:符号なし変数へのマイナス値転送
01 WS-SIGNED PIC S9(5) VALUE -500.
01 WS-UNSIGNED PIC 9(5). *> 符号なし
MOVE WS-SIGNED TO WS-UNSIGNED
*> 結果は実装依存(符号が失われ絶対値になるか、00500になるか)
*> GnuCOBOL: WS-UNSIGNED = 00500 (符号消失)
*> 対策: 必ず PIC S9(n) で符号付き宣言する
01 WS-DEST-SIGNED PIC S9(5).
MOVE WS-SIGNED TO WS-DEST-SIGNED *> OK
落とし穴2:グループ項目の上書きによるフィールド破損
01 WS-DATE-GROUP.
05 WS-YEAR PIC 9(4).
05 WS-MONTH PIC 9(2).
05 WS-DAY PIC 9(2).
01 WS-DATE-INIT PIC X(8) VALUE "00000000".
*> グループ項目全体を一括クリアしたいとき
MOVE "00000000" TO WS-DATE-GROUP *> グループをバイト単位でクリア
*> より安全な方法: 個別フィールドをMOVE ZERO
MOVE ZERO TO WS-YEAR
MOVE ZERO TO WS-MONTH
MOVE ZERO TO WS-DAY
*> または INITIALIZE を使う(詳細は別記事参照)
INITIALIZE WS-DATE-GROUP
落とし穴3:MOVE 後に元の値が残ったと思い込む
01 WS-SRC PIC X(10) VALUE "ORIGINAL".
01 WS-DEST PIC X(10).
MOVE WS-SRC TO WS-DEST
*> WS-DEST = "ORIGINAL"
*> WS-SRC = "ORIGINAL" ← 変化しない
*> MOVEはコピー操作。元データは失われない。
*> 「転送」という言葉のイメージに惑わされないこと
実践パターン集
パターン1:帳票出力用のレコード整形
01 WS-SALES-DATA.
05 WS-ITEM-CODE PIC X(10).
05 WS-ITEM-NAME PIC X(30).
05 WS-UNIT-PRICE PIC S9(7)V99 COMP-3.
05 WS-QTY PIC S9(5) COMP-3.
05 WS-SUBTOTAL PIC S9(9)V99 COMP-3.
01 WS-PRINT-LINE.
05 PL-ITEM-CODE PIC X(10).
05 FILLER PIC X(2) VALUE SPACES.
05 PL-ITEM-NAME PIC X(30).
05 FILLER PIC X(2) VALUE SPACES.
05 PL-UNIT-PRICE PIC ZZZ,ZZZ,ZZ9.
05 FILLER PIC X(2) VALUE SPACES.
05 PL-QTY PIC ZZZ,ZZ9.
05 FILLER PIC X(2) VALUE SPACES.
05 PL-SUBTOTAL PIC ZZZ,ZZZ,ZZZ,ZZ9.
*> 数値→数値編集項目へMOVE(自動整形)
MOVE WS-ITEM-CODE TO PL-ITEM-CODE
MOVE WS-ITEM-NAME TO PL-ITEM-NAME
MOVE WS-UNIT-PRICE TO PL-UNIT-PRICE
MOVE WS-QTY TO PL-QTY
MOVE WS-SUBTOTAL TO PL-SUBTOTAL
WRITE PRINT-RECORD FROM WS-PRINT-LINE
パターン2:ループ前の作業域一括初期化
01 WS-WORK-AREA.
05 WS-W-CODE PIC X(10).
05 WS-W-NAME PIC X(30).
05 WS-W-AMOUNT PIC S9(9)V99 COMP-3.
05 WS-W-COUNT PIC S9(5) COMP-3.
05 WS-W-ERR-FLG PIC X(1).
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > WS-MAX
*> ループ先頭で作業域を一括クリア
MOVE SPACES TO WS-W-CODE WS-W-NAME WS-W-ERR-FLG
MOVE ZEROS TO WS-W-AMOUNT WS-W-COUNT
*> 処理本体
PERFORM MAIN-LOGIC
END-PERFORM
パターン3:入力レコードから出力レコードへのCORR一括転送
01 FS-INPUT-RECORD.
05 FS-CUST-ID PIC X(8).
05 FS-CUST-NAME PIC X(30).
05 FS-BIRTH-DATE PIC 9(8).
05 FS-INPUT-DATE PIC 9(8). *> 出力には不要
01 WS-OUTPUT-RECORD.
05 FS-CUST-ID PIC X(8).
05 FS-CUST-NAME PIC X(30).
05 FS-BIRTH-DATE PIC 9(8).
05 WS-PROC-DATE PIC 9(8). *> 入力にはない(処理日を別途設定)
*> 同名フィールドを一括転送(INPUT-DATE と PROC-DATE は対象外)
MOVE CORRESPONDING FS-INPUT-RECORD TO WS-OUTPUT-RECORD
*> 処理日は別途設定
MOVE WS-TODAY TO WS-PROC-DATE
MOVE vs INITIALIZE の使い分け
| 用途 | 推奨手段 | 理由 |
|---|---|---|
| 特定フィールドに特定値を代入 | MOVE |
対象と値を明示できる |
| グループ項目内を型に応じてゼロ/スペースに初期化 | INITIALIZE |
数値→0、文字→スペースを自動適用 |
| グループ項目全体を同じ文字で埋める | MOVE SPACESなど |
バイト単位の一律クリアができる |
| 特定フィールドのみ初期化(残りは保持) | MOVE |
INITIALIZEは対象グループ全体に適用される |
よくある質問
MOVE ではなく SET を使います。例えば SET IS-ACTIVE TO TRUE は、IS-ACTIVEに対応する親変数に VALUE で定義した値を代入します。条件名に対して MOVE を使うとコンパイルエラーになります。ON SIZE ERROR 句を使ってください。FILLER PIC X(2) VALUE " ". のように定義時に初期値を設定する方法が一般的です。まとめ
MOVE文のポイントをまとめます。
- 文字型(X):左寄せ・右スペース埋め・右切り捨て
- 数値型(9):右寄せ・左ゼロ埋め・左切り捨て(SIZE ERRORなし)
- フィギュアリティブ:ZERO・SPACE・HIGH-VALUE・ALL “X” を用途に応じて使い分け
- MOVE CORRESPONDING:同名フィールドを一括転送。型変換は通常のMOVEと同じルール
- グループ項目:バイト単位コピーのため型変換なし。構造が異なると意図しない結果になる
- 数値編集項目:帳票用の表示形式(カンマ・ゼロ抑制)への一方向変換
- 落とし穴:符号消失・桁切り捨て・バイト単位グループ上書きに注意
関連記事:INITIALIZE文を使ってデータ項目を初期化する方法、COMPUTEで計算を行う(ROUNDED・ON SIZE ERROR)、REDEFINES句で項目を再定義する方法、DISPLAY文で画面にメッセージやデータを表示させる方法