やりたかったこと
セキュリティエリア内で自由に使える開発言語が(悲しいことに)EXCEL-VBAぐらいしかない案件があり、C言語やPerlで自作したツールを持ち込めそうにないので、VBAでバイナリファイル(某社COBOLファイル)を扱えるものを作った。
残念なこと
EXCEL-VBAでは、2G以上のファイルを扱えない。
2Gまでは処理を進めるが、突然エラーMSGを出して止まってしまう。内部ではファイルポインタを使っているが、32ビット(2^31)になっていると想像。
アウトライン
- UI:EXCELブックから、マクロを起動。マクロでEXCELシートから入力・出力・ログのファイル名やファイル属性を取得し、マクロ(sub)を起動する。
- 主たるマクロ
- ex_byte2hex : バイナリファイルを16進テキストファイルに変換
- ex_hex2byte : 16進テキストファイルをバイナリファイルに変換
- vbaのコードはgistに置いてある
EXCEL-VBAでバイナリファイル
EXCEL
ボタン
ボタンに、ex_byte2hex
, ex_hex2byte
を割り当てる
固定長のCOBOLファイル
固定長[recfm=f]を指定した時
-
ex_byte2hex
は、lreclバイトを1レコードとして[lrecl*2の16進文字列+改行(CR,LF)]のテキストファイル1行にする。 -
ex_hex2byte
は、テキストファイル1行[16進文字列+改行(CR,LF)]を、2文字毎にバイト列とし固定長COBOLファイル化する。
16進テキスト(固定長)
0C000000303132331020304C41424344
0C00000010000000313233341020304D
42434445012345671000000010000000
323334352030405C4344454612345678
100000000C000000333435362030405C
444546470C0000001000000034353637
3040506C454647482345678910000000
可変長のCOBOLファイル
可変長(recfm=v)を指定した時
-
ex_byte2hex
は、COBOL可変長の1レコード[LL + バイト列 + LL]を16進テキスト1行にする。 -
ex_hex2byte
は、テキスト1行[16進文字列+改行(CR,LF)]を2文字ごとにバイト列にして、COBOL可変長の1レコード[LL + バイト列 + LL]にする。 - 某社のCOBOL可変長ファイルは、LLが[2byteのLittleEndianのbinary + 0x0000]なので、VBAでは4byteのLittleEndianであるLong型を使っている。
16進テキスト(可変長)
303132331020304C41424344
313233341020304D4243444501234567
323334352030405C4344454612345678
333435362030405C44454647
343536373040506C4546474823456789
バイナリファイルを扱う
オープン・入力
binary-open-read
fno = FreeFile
Open f.fname For Binary Access Read As #fno
オープン・出力
binary-open-write
fno = FreeFile
Open f.fname For Binary Access Write As #fno
読み込み(Byte型)
Byte属性の配列を定義し、その要素数バイトを読み込む
binary-get
' 読み込みサイズ分のバイト列を用意
Dim bBuf() As Byte
ReDim bBuf(0 To lrecl - 1)
'
' lrecl(bBuf)バイトを読む
'
' ============================================================
Get #fno, , bBuf
' ============================================================
If EOF(fno) = True Then
f.iseof = FCNTL_EOF
End If
読み込み(Long型)
4バイトの整数を読み込む例。可変長COBOLファイルのレコード長(LL)がこの型なので。
binary-getLL
Dim ll As Long
Get #fno, , ll
'
If EOF(fno) = True Then
f.iseof = FCNTL_EOF
End If
書き込み(バイト型)
Byte属性の配列を定義し、その要素数バイトを書き込む
binary-write
Dim buf() As Byte
ReDim buf(0 To lrecl - 1)
Dim i As Integer
For i = 0 To lrecl - 1
buf(i) = bytes(i)
Next i
' ================================
Put #fno, , buf
' ================================
書き込み(整数、Long)
4バイト整数の書き込み。可変長COBOLのレコード長(LL)がこの型なので。
binary-writeLL
Dim lrecl as Long
'
fno = f.fileno
Put #fno, , lrecl
'
テキストファイルを扱う
ログファイルは無条件にテキストファイルで扱う。
オープン・入力
text-open-input
fno = FreeFile
Open f.fname For Input As #fno
オープン・出力
text-open-output
fno = FreeFile
Open f.fname For Output As #fno
読み込み(テキスト)
text-input
' テキスト1行を読込。最終行を読み込んだ時に、EOFが帰ってくるので、EOFでも返却域の設定を行う
' ================================================================
Line Input #fno, buf
' ================================================================
If EOF(fno) = True Then
f.iseof = FCNTL_EOF
End If
書き込み(テキスト)
text-print
fno = f.fileno
Print #fno, buf
バイナリ・テキスト共通
クローズ
close
Close #fno
ファイル属性の構造体
上のソースで、f.xxx
はこの構造体のメンバ
Fcntl
Type Fcntl
fileno As Integer ' ファイル番号。#をつけて参照
fname As String ' ファイル名
recfm As String ' f|v|t / 固定長(f)、可変長(v)、テキスト(t)
lrecl As Integer ' 固定長:レコード長、可変長:最大長
isopened As Integer ' 0|1 / 0:NotOpened, 1:Opened
iocnt As Long ' 入出力件数
mode As String ' i|o / i:inpupt, o:output
maxbuf As Long ' bufの最大、inputは3200, outputは64000
iseof As Integer ' 0:notEOF, 1:EOF
End Type