Vladimir F 是正确的,您希望在 Fortran 中“流”访问“原始二进制”文件。这是一个 MWE:
Python
import numpy as np
A = np.random.rand(10000, 10000)
print(A.sum())
A.tofile('data.bin')
Fortran
program read_unformatted_binary_from_python
use iso_c_binding
implicit none
integer, parameter :: DPR = selected_real_kind(p=15)
character(len=*), parameter :: filename = 'data.bin'
integer, parameter :: N = 10000
real(DPR), allocatable, dimension(:, :) :: dat
allocate(dat(N, N))
open(40, file=filename, status='old', access='stream', form='unformatted')
read(40) dat
close(40)
write(*,*) sum(dat)
end program read_unformatted_binary_from_python
我的 Fortran 示例可能比必要的要长一些,因为我使用许多不同的系统和编译器套件,并且也讨厌大型静态数组(毕竟我是 Fortran 用户)。
我在 MacBook Pro 上使用 Homebrew GCC 6.3.0_1 中的 Python 2.7.x、Numpy 13.x 和 gfortran 快速编写了此代码,但这应该适用于所有系统。
更新:
这里需要特别注意数组的形状和大小。如果dat
分配的大小大于文件中的大小,大于流的大小read
应该尝试填充整个数组,点击EOF
符号,并发出错误。在 Python 中,np.fromfile()
方法将读取到EOF
然后返回一个具有适当长度的一维数组,即使A
原本是多维的。这是因为原始二进制文件没有元数据,只是 RAM 中的连续字节字符串。
结果,Python 中的以下几行生成相同的文件:
A = np.random.rand(10000, 10000)
A.tofile('file.whatever')
A.ravel().tofile('file.whatever')
A.reshape((100, 1000, 1000)).tofile('file.whatever')
该文件可以被读入并重塑为:
B = np.fromfile('file.whatever').reshape(A.shape)
B = np.fromfile('file.whatever').reshape((100, 1000, 100, 10))
# or something like
B = np.fromfile('file.whatever') # just a 1D array
B.resize(A.shape) # resized in-place
在 Fortran 中,使用流访问很容易读取整个原始文件,而无需提前知道其大小,尽管您显然需要某种用户输入来重塑数据:
program read_unformatted_binary_from_python
use iso_c_binding
implicit none
integer, parameter :: DPR = selected_real_kind(p=15)
character(len=*), parameter :: filename = 'data.bin'
integer :: N = 10000, bytes, reals, M
real(DPR), allocatable :: A(:,:), D(:, :), zeros(:)
real(DPR), allocatable, target :: B(:)
real(DPR), pointer :: C(:, :)
allocate(A(N, N))
open(40, file=filename, status='old', access='stream', form='unformatted')
read(40) A
write(*,*) 'sum of A', sum(A)
inquire(unit=40, size=bytes)
reals = bytes/8
allocate(B(reals))
read(40, pos=1) B
write(*,*) 'sum of B', sum(B)
! now reshape B in-place assuming the user wants the first dimension
! (which would be the outer dimension in Python) to be length 100
N = 100
if(mod(reals, N) == 0) then
M = reals/N
call C_F_POINTER (C_LOC(B), C, [N, M])
write(*, *) 'sum of C', sum(C)
write(*, *) 'shape of C', shape(C)
else
write(*,*) 'file size is not divisible by N!, did not point C to B'
end if
! now reshape B out-of-place with first dimension length 9900, and
! pad the result so that there is no size mismatch error
N = 9900
M = reals/N
if(mod(reals, N) > 0) M=M+1
allocate(D(N, M))
allocate(zeros(N), source=real(0.0, DPR))
D = reshape(B, [N, M], pad=zeros)
write(*,*) 'sum of D', sum(D)
write(*,*) 'shape of D', shape(D)
! obviously you can also work with shape(A) in fortran the same way you
! would use A.shape() in Python, if you already knew that A was the
! correct shape of the data
deallocate(D)
allocate(D, mold=A)
D = reshape(B, shape(A))
write(*,*) 'sum of D', sum(D)
write(*,*) 'shape of D', shape(D)
! or, just directly read it in, skipping the whole reshape B part
read(40, pos=1) D
write(*,*) 'sum of D', sum(D)
close(40)
end program read_unformatted_binary_from_python