COPY句レイアウト表の桁位置計算方法
今回の目的
今回のこの記事の目的はCOPY句レイアウト表の桁位置を算出し値を編集すること。そして最後に全体のレコード長を計算し編集すること。これをExcel VBAで構築する。やりたいことのイメージは以下のとおり。

全体のレコード長は項目名見出し(B1)の横に(レコード長:9999)の形式で編集することとする。
ツールの構成イメージ
桁位置計算は、レコードレイアウトに追加・修正・削除を行った場合にボタン一つで再計算できるように、独立した機能として作成する。全体のイメージとしては以下の図のとおり。

ちゃちゃっとフローチャートを作ってみたが、これも若い世代の技術者に見せてみあまりピンとはこないらしい。今回のツール程度であれば必要ないかもしれないが、もう少し複雑なツール(例えばExcelでパラメタを作って汎用機に転送してJCLを実行し、その結果をWindowsに受信してExcelに取り込む等)を作る場合、個人的には必須資料である。
機能の定義
上記図のとおり、主な機能は2つとする。
- COPY句テキストファイルを読み込んで、新たにCOPY句レイアウト表を作成する。
- COPY句レイアウト表を読み込んで、桁位置を再計算する。
今回の解説部分(COPY句レイアウト表の先頭から各項目の桁位置を算出し編集する)
今回仕組みを解説する部分は以下のとおり。
「COPY句レイアウト表の先頭から各項目の桁位置を算出し編集する」
機能1、機能2どちらでも利用する。
COPY句レイアウト表の桁位置計算方法
約25年前に自分が作ったロジック
さて、やっと本題にたどり着いた。
これから桁位置の計算方法を説明するのだが、自分で作ったとは言え何しろ約25年前に作成したツールである。先日、桁位置計算部分のロジックを眺めてみたが、正直25年前の自分が何を考えていたのかよく分からない。この記事は自宅のPCで記載しているのだが、自分で作っているとはいってもツールを外部に持ち出すのはコンプラ上良くないので、桁位置計算部分だけ紙に印刷して持ってきた。(これはコンプラ違反ではないのか?というご意見もあるかもしれないが、自分が作ったツールの一部を印刷して持ち出すのは大丈夫じゃないか...いやいや職場で印刷した紙を自宅に持ち帰るのは...と色々考えたが良く分からないのでヨシとしよう)
自分の実力
私のExcel VBAの知識は完全に「独学」である。忘れもしない、真ん中の子が生まれた時に転職して現在の職場に配属となった。手作業が多く「めんどくさいなぁ」と思いつつ、様々な手作業を何とか自動化しようとしてExcel VBAに手を出したのがきっかけだった。情報源は仕事の帰りに本屋に寄ってExcel VBAの本を立ち読みするか、よく解らないExcelのHELPを読みながらだった。
最近初めてExcel VBAの本「Excel VBAを実務で使い倒す技術(秀和システム新社刊)」を購入してみたが、さすがに新たに得るものはそんなには無かった。ただし、チームでExcel VBAの開発をしたことがないので、共通化とかコードの標準化とかそのあたりは弱点かも。
AI(無課金Gemini)の実力
これを書いている現在(2025/12/29)、GeminiにCOPY句テキストファイルやExcel表を渡し、詳細に指示を出してVBAのロジック作成を依頼したところ、全くイケてない桁位置計算ロジックが出てきた。もしかしたら指示の方がダメだったのかもしれないが、幾度かトライしても正しい桁位置編集で、かつ正しいレコード長を得ることができなかったので諦めた。まだ、約25年前の自分の方が賢いかも笑
正しい桁位置計算方法の流れ
正しい桁位置を計算するための方法として、REDEFINES句とOCCURS句の定義がポイントとなることは既に記載した。こちらを参照。計算しようとしている項目が現在どの配下にいるのか?もっと具体的に書くと、
- どのREDEFINES項目の配下にいるか?
- どのOCCURS項目の配下にいるか?
これが重要になる。そして、ロジックとしては以下のような流れになる。
- Excel表を先頭(2行目)から順に検索しA列が空欄になるまで繰り返す
- REDEFINES(E列)にREDEFINES元項目名が出現したら「制御配列」に登録する
- OCCURS(D列)に数字が出現したら「制御配列」に登録する
- 開始桁位置を編集する
- 次行桁位置を算出する
- 現在行をカウントアップする
- 現在行の設定値で管理配列の終了判定を行う
桁位置を編集するExcel表
桁位置算出を行うExcel表を再掲する。以下表の2行目から開始。

制御配列の構造体定義と各種管理変数の定義
制御配列は構造体を使用する。コードは以下のとおり。
Option Explicit
・・・
'制御情報の構造体定義
Type ControlArray
Str_Category As String '区分。R:REDEFINES,O:OCCURSを示す
Int_Level As Integer 'LEVELを数値化した値
Int_OccursNum As Integer '区分=Oの場合「回数」を管理
Lng_RedefinesPos As Integer '区分=Rの場合「再定義元次の桁位置」を管理
Int_NowOccursNum As Integer '区分=Oの場合の現在の繰り返し数を管理
Lng_Row As Long 'REDEFEINS,OCCURSが出現したExcel行位置
End Type
'制御配列(要素数10)のオブジェクト定義
Dim CA(9) As ControlArray
'各種制御変数
Dim Idx1 As Long '制御配列Index
Dim Lng_NowRow '現在Excel行位置
Dim Lng_NextDigitPos '次行桁位置
・・・
変数の命名規則やコメントの記載方法、データ型の選択等については異論を認める。約25年前にこのコーディング方法で定着してしまったので今更変えるのも面倒だ。
制御配列CAは要素数が10もあれば事足りるはず。そしてグローバル変数として定義する。COBOLシステムに携わった技術者なら理解できると思うが、COBOLはDATA DIVISIONで定義した変数全てがグローバル変数であり、モジュール内のどこからでもアクセスが可能である。
制御配列と制御変数のイメージはこんな感じ。

Excel VBAに限らず様々なプログラミング本を読むと「マジックナンバー」は使用しない方が良いと書いてあり、AI(GeminiやCopilot)に自分で作ったロジックを添削してもらうと、必ず「マジックナンバー」の使用を指摘される。上記のコードだと制御配列の要素数10の部分「9」が該当する。言われてみれば確かにそうではあるのだが、チームで開発しているわけではないしユーザーに納品するプログラムでもないし、誰かにメンテナンスを依頼するプログラムでもない。であれば好きに作れば良いのではないだろうか。いままでそれで困ったことが無いので自分が納得して、そして動けば良い。ただ、必ずコメントは書くようにしている。(コメントも、過去の自分が嘘書いてたりするので注意しないとならないのだが...)
不勉強で申し訳ないが、最新のCOBOL言語+コンパイラ環境を知らないので「富士通汎用機COBOL」で極一般的な定義方法での説明となる。(フリーで使えるCOBOL言語+コンパイラーを見たことがない。また大概の場合マニュアルも有料であるため詳細に調べてもいない。今回の記事を書くにあたりネットでCOBOLのマニュアルを調べたところ、富士通のNetCOBOLのマニュアルがヒットした。ただ、COBOLのマニュアルは難しいので読むのが面倒で読んでいない)
制御配列をグローバル変数としたのは、どのSubやFuncからでも参照・更新できるようにするため。グローバル変数は、COBOLに慣れているとあまり違和感がないのが正直なところ。まあ、単独で使用する予定のツールなので、変数のスコープに悩むくらいだったらグローバル変数にしてしまった方が早い。
また、制御配列とは別に「制御配列添字、現在Excel行、次行桁位置」の3つをグローバル変数として定義しておく。
机上デバッグ
COBOLシステムに携わっていると「机上デバッグ」というワザがたまに出現する。若い技術者はほとんどやらないかもしれないが、特に込み入った処理や複数の配列や添え字を扱う場合で、間違うと面倒なことになる場合、個人的には必ず机上デバッグを行う。いわゆるシミュレーションである。もちろん頻繁にはやらない。特にExcel VBAはインタプリタなので1行毎に実行が可能だ。机上でシミュレーションやるよりは実行していった方が早い。
使う場面は、COBOLシステムで例えば別のプログラムを呼んで、そのプログラムから予期しないエラーが返却されてきた場合(今でいうと例外処理に当たる)のエラー処理が正しく機能するかどうか?を確認するために机上デバッグを行う。意図的にエラーを起こすことが困難な場合によく使う手だ。
今回のツールで使用するかどうかは微妙なところではあるが、説明のための記事なので机上デバッグをやってみることにする。自分の頭の中を整理することを目的に、処理の流れを再掲し、もう少し詳細にやるべきことを記載する。
処理の流れ(詳細)
1.Excel表を先頭(2行目)から順に検索する
2.REDEFINES(E列)にREDEFINES元項目名が出現したら制御配列に登録する
a. Idx1 = Idx1 + 1
b. 区分(Idx1) = "R"、LEVEL(Idx1)、EXCEL行位置(Idx1) を編集
c. 「再定義元次の桁位置(Idx1)」に「次行桁位置」を編集
d. 「次行桁位置」を以下のように取得する
現在位置から上方にREDEFINES項目元を検索し、その桁位置を「次行桁位置」に編集。
3.OCCURS(D列)に数字が出現したら制御配列に登録する
a. Idx1 = -1 の場合、制御配列に登録する
ア. Idx1 = Idx1 + 1
イ. 区分(Idx1) = "O"、LEVEL(Idx1)、EXCEL行位置(Idx1) を編集
ウ. OCCURS回数(Idx1) = OCCURS(D列)の値、現在の繰返数(Idx1) = 1 を編集
b. Idx1 ≠ -1 の場合
ア. Excel行位置(Idx1)=現在Excel行位置 の場合、繰り返し中のため何もしない
イ. Excel行位置(Idx1)≠現在Excel行位置 の場合、制御配列に登録する
1) Idx1=Idx1 + 1
2) 区分(Idx1) = "O"、LEVEL(Idx1)、EXCEL行位置(Idx1) を編集
3) OCCURS回数(Idx1) = OCCURS(D列)の値、現在の繰返数(Idx1) = 1 を編集
4.開始桁位置を編集する(既に開始桁位置が編集済の場合は編集しない)
a. 「次行桁位置」を「桁位置」に編集する
5.次行桁位置を算出する
a. 次行桁位置 = 次行桁位置 + 項目桁数(属性・桁 毎に算出)
6.現在行をカウントアップする
a. 現在Excel行位置 = 現在Excel行位置 + 1
7.現在行の設定値で管理配列の終了判定を行う
a. 繰り返し
ア. LVL ≦ LEVEL(Idx1) の場合
1) 区分 = "R" の場合
a) 「再定義元次の桁位置」を「次行桁位置」に編集
b) 制御配列(Idx1)をクリア(初期化)する
c) Idx1 = Idx1 - 1
2) 区分 = "O" の場合
a) OCCURS回数(Idx1) > 現在の繰返数(Idx1) の場合
ア) 現在の繰返数(Idx1) = 現在の繰返数(Idx1) + 1
イ) 現在Excel行位置 = Excel行位置(Idx1)
b) OCCURS回数(Idx1) ≦ 現在の繰返数(Idx1) の場合
ア) 制御配列(Idx1)をクリアする
イ) Idx1 = Idx1 - 1
イ. LVL > LEVEL(Idx1)の場合
1) 区分 = "R" の場合
a) 直前に出現したREDEFINES項目配下とみなし何もしない。
2) 区分 = "O" の場合
a) 直前に出現したOCCURS項目配下とみなし何もしない。
机上デバッグを始めようと思うが、文字数が多いため次の記事に記載する。