EXCEL-VBAでバイナリファイル。COBOLファイル。

## やりたかったこと
セキュリティエリア内で自由に使える開発言語が（悲しいことに）EXCEL-VBAぐらいしかない案件があり、C言語やPerlで自作したツールを持ち込めそうにないので、VBAでバイナリファイル（某社COBOLファイル）を扱えるものを作った。

## 残念なこと
EXCEL-VBAでは、２G以上のファイルを扱えない。
２Gまでは処理を進めるが、突然エラーMSGを出して止まってしまう。内部ではファイルポインタを使っているが、３２ビット(2^31)になっていると想像。

## アウトライン
- UI：EXCELブックから、マクロを起動。マクロでEXCELシートから入力・出力・ログのファイル名やファイル属性を取得し、マクロ（sub)を起動する。
- 主たるマクロ
    - ex_byte2hex : バイナリファイルを１６進テキストファイルに変換
    - ex_hex2byte : １６進テキストファイルをバイナリファイルに変換
- vbaのコードはgistに置いてある
[EXCEL-VBAでバイナリファイル](https://gist.github.com/harasakih/7469110c24e219f8e9ae0fdfd65f5e11)

## EXCEL
### ボタン
ボタンに、```ex_byte2hex``` , ```ex_hex2byte``` を割り当てる

### 固定長のCOBOLファイル
![スクリーンショット 2024-01-28 17.13.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384104/843b4d2c-e848-5c92-9cd1-d1dec9ed4061.png)

#### 固定長[recfm=f]を指定した時
- ```ex_byte2hex```は、lreclバイトを１レコードとして[lrecl*2の１６進文字列＋改行(CR,LF)]のテキストファイル１行にする。
- ```ex_hex2byte```は、テキストファイル１行[１６進文字列＋改行(CR,LF)]を、２文字毎にバイト列とし固定長COBOLファイル化する。

```text:１６進テキスト(固定長)
0C000000303132331020304C41424344
0C00000010000000313233341020304D
42434445012345671000000010000000
323334352030405C4344454612345678
100000000C000000333435362030405C
444546470C0000001000000034353637
3040506C454647482345678910000000
```

### 可変長のCOBOLファイル

![スクリーンショット 2024-01-28 17.25.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384104/53337a0b-ee2e-dd6a-130c-7cbb20a62420.png)

#### 可変長(recfm=v)を指定した時
- ```ex_byte2hex```は、COBOL可変長の１レコード[LL + バイト列 + LL]を１６進テキスト１行にする。
- ```ex_hex2byte```は、テキスト１行[１６進文字列＋改行(CR,LF)]を２文字ごとにバイト列にして、COBOL可変長の１レコード[LL + バイト列 + LL]にする。 
- 某社のCOBOL可変長ファイルは、LLが[2byteのLittleEndianのbinary + 0x0000]なので、VBAでは4byteのLittleEndianであるLong型を使っている。


```text:１６進テキスト(可変長)
303132331020304C41424344
313233341020304D4243444501234567
323334352030405C4344454612345678
333435362030405C44454647
343536373040506C4546474823456789
```

## バイナリファイルを扱う
### オープン・入力
```vb:binary-open-read
fno = FreeFile
Open f.fname For Binary Access Read As #fno
```

### オープン・出力
```vb:binary-open-write
fno = FreeFile
Open f.fname For Binary Access Write As #fno
```

### 読み込み(Byte型)
Byte属性の配列を定義し、その要素数バイトを読み込む

```vb: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型)
４バイトの整数を読み込む例。可変長COBOLファイルのレコード長(LL)がこの型なので。
```vb:binary-getLL
    Dim ll As Long
    Get #fno, , ll
'
    If EOF(fno) = True Then
        f.iseof = FCNTL_EOF
    End If
```

### 書き込み（バイト型）
Byte属性の配列を定義し、その要素数バイトを書き込む

```vb: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)
４バイト整数の書き込み。可変長COBOLのレコード長(LL)がこの型なので。
```vb:binary-writeLL
    Dim lrecl as Long
'
    fno = f.fileno
    Put #fno, , lrecl
'
```

## テキストファイルを扱う
ログファイルは無条件にテキストファイルで扱う。

### オープン・入力
```vb:text-open-input
fno = FreeFile
Open f.fname For Input As #fno
```

### オープン・出力
```vb:text-open-output
fno = FreeFile
Open f.fname For Output As #fno
```

### 読み込み（テキスト）
```vb:text-input
' テキスト１行を読込。最終行を読み込んだ時に、EOFが帰ってくるので、EOFでも返却域の設定を行う
' ================================================================
    Line Input #fno, buf
' ================================================================
    If EOF(fno) = True Then
        f.iseof = FCNTL_EOF
    End If
```

### 書き込み（テキスト）
```vb:text-print
    fno = f.fileno
    Print #fno, buf
```

## バイナリ・テキスト共通
### クローズ
```vb:close
Close #fno
```

## ファイル属性の構造体
 上のソースで、`f.xxx`はこの構造体のメンバ

```vb: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
```