LoginSignup
5
4

More than 1 year has passed since last update.

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

Posted at

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

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として保存して、

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を使うことにします。まず、バイナリファイルを読むためには、

std::string filename = "test.dat";
std::ifstream fin(filename, std::ios::in | std::ios::binary );

のようにstd::ifstreamのオブジェクトを使います。整数を読み込む関数は

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文で書いた変数のサイズが格納されています。ですので、このサイズiiread( ( char * ) &ii2, ii );を使ってバイナリを読み込みます。

倍精度実数は

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>を使って読み込みます。例えば、

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で調べています。

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

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で書き込んだバイナリを読むためのコードは

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として保存して、

g++  -std=c++17 read.cpp

でコンパイルして実行すると、

1
2 7 
0.540302
2.1 3.2 0.909297 

となり、ちゃんと読み込めていることがわかります。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4