2022/07/25

固定長ファイルのテキストデータ呼び込み時処理

固定長ファイルをデータベースとしたシステムでは、「固定長ファイル」を使用します。イメージとしては図1のように、1つのデータの長さ(バイト単位)が一定で、データの数(図1では13個)の分だけ繋がっているファイルです。(ハードディスクの円盤を思い浮かべると、近いかもしれません)
1データの長さは、あらかじめ決まっていますので、「何番目のデータか」さえ分かれば「ファイル先頭(BoF)から、何バイト目からのデータだ」と分かり、データの取り出し・書き込みが素早くできるという特長があります。

固定長ファイルのイメージ
図1


固定長ファイルではデータ長(バイト数)が決まっていますので、それよりも長いデータは入りません。またデータが短ければ、残りは「半角スペース」で埋められることになります。
また、そのデータを取り出す時は「1つのデータを丸ごと(短い場合はスペースも含めて)」取り出しますし、また書き込む時も「1つのデータを丸ごと」書き込みます。
そして固定長ファイル内では、「全角文字=2バイト」「半角文字=1バイト」のサイズを使用します。

ExcelのVBAを使ってデータを取り出した後は、取り出した先(Excel側)のルールで文字列を取り扱う必要があります。固定長ファイル内では「全角文字=2バイト」「半角文字=1バイト」というルールでしたが、Excel内では文字列を「2バイト文字セット(DBCS:Double-byte Character Sets)」という扱い方をしています。これは「半角でも全角でも2バイトを使用」するというルールです。

例えば、固定長の文字数を「半角20文字」と設定した時を考えます。半角20文字ですから固定長ファイル内には20バイト単位でデータが存在している事になります。この中に「ABCDEFGHIJ」という「半角で10文字」のデータがあり、それを取り出したとします。半角10文字は10バイトですから、残り10バイトは「スペース(半角)」で埋められていることになります。
そのデータを受け取るExcel側では「半角20文字は40バイト」というサイズが必要です。

ここで、受け取るバイト数をあらかじめ決めている「Excel側の変数」に違和感を持つかもしれません。データを受け取ったら、受け取ったサイズに合わせて形を変えてくれるのがExcelの良いところです。
しかし、今回受け取るのは「1データ」というまとまりで、その中には例えば3つの項目が入っている場合もあります。個別項目(Excelで言えば3列分)も、サイズはそれぞれ固定ですので、全体として「あらかじめ決めたサイズで受け取る」必要があるのです。

図2は、固定長ファイルの文字列をExcelの文字列に変換した時の様子を示したもので、上段が固定長ファイル(20バイト)、下段がExcel側(40バイト)です。半角の単位が1バイトから2バイトに単純に変換するだけですので、「ABCDEFGHIJ」という「半角で10文字」のデータは、スペースも含めてExcel側で受け取れます。

半角のみの場合
図2


図2は半角のみの場合でした。では次に「全角が含まれる場合」を考えます。例えば「ひらがな5文字(あ~お)+半角英数字5文字(A~E)」です。
「ひらがなは全角」ですから、固定長ファイル内でも2バイトずつ使いますので、合計15バイトを使い、残り5バイトがスペースで埋められています。このデータをExcel側に持ってきた時の様子が図3になります。

全角が入った場合
図3


Excel側の受け口は「半角20文字 = 40バイト」と変わりませんが、全角文字の部分は2バイト→2バイトと「バイト数の変化が無い」ので、1文字1文字変換していくと「5文字分(Excel側で10バイト分)余ってしまう」ことになります。
文字を受取る側のExcelでは「文字数を決める変数を宣言(図4の01~03行目)」した場合「20文字分=40バイトが全てNull」という態勢で待っているのに、30バイト分しか文字が来ないので、来なかった5文字分は「Nullのまま残ってしまう」ことになります。

Excel側としては、受け取った文字列の後ろに「スペース+Null」がくっついているのは都合が悪いため、取り除く必要があります。しかしTrim関数では取り除けないため「Replaceメソッドで、Nullを""(長さゼロの文字列)に置換」するなどの前処理が必要です。
固定長ファイルからデータを取り出し、Nullの置換・スペースの削除処理までの流れが図4のコードになります。
  1. Type Record
  2.  A As String * 20    '←A項目の長さを指定
  3. End Type
  4. Sub its005_01()
  5.  Dim R As Record    '←ユーザー定義型変数を設定
  6.  fileNo = FreeFile    '←空いているファイル番号取得
  7.  Open [固定長ファイルのパス+ファイル名] For Random As #fileNo Len = Len(R)
  8.   Get #fileNo, [取り出すデータ位置], R
  9.   [取得データ] = Trim(Replace(R.A, Chr(0), ""))
  10.  Close #fileNo    '←ファイルを閉じる
  11. End Sub
図4


01~03行目では、ユーザー定義型変数(=固定長ファイル内の1データの内訳)の宣言をしています。02行目「A As String * 20」では、データ内のAという項目の長さを「半角20文字」に設定しています。
もし、1データの中に複数の項目がある場合には、このユーザー定義型変数の項目として長さを指定して列記します。

05~15行目のプロシージャ内で、固定長ファイルからデータを取得しています。
まず06行目「Dim R As Record」で、データを受け取るための変数(R)を宣言します。この変数は01~03行目で宣言したユーザー定義型変数の型です。

08行目「fileNo = FreeFile」で、現在空いているファイル番号を取得し、変数fileNoとします。
09行目「Open [固定長ファイルのパス+ファイル名] For Random As #fileNo Len = Len(R)」で、固定長ファイルを開きます。開く際には「1データの長さ」を「Len = 」の後に指定します。今回は項目A(20文字)のみですが、項目が複数ある場合にはその合計値が指定されることになります。
11行目「Get #fileNo, [取り出すデータ位置], R」で、指定したデータ位置から1データを取り出し、1データ全部の内容(複数項目の時も全項目)を変数Rに代入します。

変数Rは、データ全体を表していますので、データそのものにアクセスするためには「R.A」などと「Rの中のA項目」を指定する必要があります。その項目には前述の通り「Nullやスペース」が含まれている可能性があるため、12行目「[取得データ] = Trim(Replace(R.A, Chr(0), ""))」で処理をします。
まず「Replace(R.A, Chr(0), "")」の部分でNull(文字コード=&H0)を「""(長さゼロの文字列)」に置換します(スペースに置換してもOKです)。
そしてNullを削除した後、Trim関数でスペースを削除します。最初にNullを削除する理由は、Trim関数は「Nullを削除しない」ため、Nullと本当の文字列とに挟まれたスペースが残ってしまうためです。

この処理をして取り出した12行目の「取得データ」は、両端にスペースもNullも無いデータとなります。
最後に14行目「Close #fileNo」でファイルを閉じます。

なお、データとして半角しか扱わないシステムでしたら、このような処理は不要になります。

アプリ実例

共有コメント付きカレンダー