Fortranで作ったバイナリをc++で読み込む

Fortranで作られたバイナリをc++から読む必要が生じたので、コードを書いてみました。

# Fortranコード

バイナリを作成するFortranのコードは

```fortran:binary.f90
program main
    implicit none
    integer::i,j,k
    real(8)::a
    real(8)::vec(3)
    i = 1
    j = 2
    k = 7
    a = cos(1d0)
    vec(1) = 2.1d0
    vec(2) = 3.2d0
    vec(3) = sin(2d0)

    open(11,file="test.dat",form="unformatted")
    write(11) i
    write(11) j,k
    write(11) a
    write(11) vec

    close(11)
end program
```

です。このコードを```binary.f90```として保存して、

```bash
gfortran binary.f90   
```

でコンパイルして実行すると、```test.dat```というバイナリファイルができます。
バイナリの構造ですが、
https://akebi28.hatenablog.jp/entry/2017/02/28/081307
によると、Fortranはwrite文を書くたびにrecord markerなる4バイトのバイナリをwriteの前後に付け加えているようです。この4バイトに注意して読み込む必要があります。


# C++で読む
作ったファイルの中身をC++で読んでみましょう。C++はまだ勉強中であまりよくわかっていませんが、C++17を使うことにします。まず、バイナリファイルを読むためには、

```c++
std::string filename = "test.dat";
std::ifstream fin(filename, std::ios::in | std::ios::binary );
```
のように```std::ifstream```のオブジェクトを使います。整数を読み込む関数は

```c++
int read_int(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);
    int ii2; 
    fin.read( ( char * ) &ii2, ii ); 

    fin.read( ( char * ) &ii, 4);
    return ii2;
};
```

とします。ここで、```fin.read( ( char * ) &ii, 4)```は、Fortranのwrite文の前についている4バイトの情報を読み込むものです。この4バイトには、write文で書いた変数のサイズが格納されています。ですので、このサイズ```ii```で```read( ( char * ) &ii2, ii ); ```を使ってバイナリを読み込みます。

倍精度実数は

```c++
double read_double(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);

    double x; 
    fin.read( ( char * ) &x, ii ); 

    fin.read( ( char * ) &ii, 4);

    return x;
};
```

は同じ感じでこれで読めます。

Fortranで```write(11) j,k```のように複数の整数が並んでいるものを読み込む場合には、```std::vector<int>```を使って読み込みます。例えば、

```c++
std::vector<int> read_multiint(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);

    int num = ii / 4;
    std::vector<int> x(num); 
    fin.read( ( char * ) &x[0], ii ); 

    fin.read( ( char * ) &ii, 4);

    return x;
};
```

とします。ここで、整数は4バイトなので、書かれているバイト```ii```を4で割ることで何個の要素があるかを```num = ii / 4 ```で調べています。

複数の倍精度実数も同様に

```c++
std::vector<double> read_multidouble(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);

    int num = ii / 8;
    std::vector<double> x(num); 
    fin.read( ( char * ) &x[0], ii ); 

    fin.read( ( char * ) &ii, 4);

    return x;
};
```
で読み込めます。倍精度実数は8バイトなので```num = ii /8```となっています。

結局、Fortranで書き込んだバイナリを読むためのコードは

```c++:read.cpp
#include <string> 
#include <fstream>
#include <iostream>
#include <vector>

int read_int(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);
    int ii2; 
    fin.read( ( char * ) &ii2, ii ); 

    fin.read( ( char * ) &ii, 4);
    return ii2;
};

std::vector<int> read_multiint(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);

    int num = ii / 4;
    std::vector<int> x(num); 
    fin.read( ( char * ) &x[0], ii ); 

    fin.read( ( char * ) &ii, 4);

    return x;
};

double read_double(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);

    double x; 
    fin.read( ( char * ) &x, ii ); 

    fin.read( ( char * ) &ii, 4);

    return x;
};

std::vector<double> read_multidouble(std::ifstream &fin){
    int ii;  
    fin.read( ( char * ) &ii, 4);

    int num = ii / 8;
    std::vector<double> x(num); 
    fin.read( ( char * ) &x[0], ii ); 

    fin.read( ( char * ) &ii, 4);

    return x;
};


int main(int argc, char *argv[]){
    std::string filename = "test.dat";
    std::ifstream fin(filename, std::ios::in | std::ios::binary );

    int i = read_int(fin);
    std::cout << i << std::endl;

    std::vector<int> x = read_multiint(fin);
    for(auto v: x){
        std::cout << v << " ";
    };
    std::cout << std::endl;

    double a = read_double(fin);
    std::cout << a << std::endl;

    std::vector<double> y = read_multidouble(fin);
    for(auto v: y){
        std::cout << v << " ";
    };
    std::cout << std::endl;

};
```

と書けます。このコードを```read.cpp```として保存して、

```bash
g++  -std=c++17 read.cpp
```
でコンパイルして実行すると、

```
1
2 7 
0.540302
2.1 3.2 0.909297 
```
となり、ちゃんと読み込めていることがわかります。