1

バイナリ ファイルを fortran プログラムに渡すシェル スクリプトがあります。

Mth=$1
loop=1
it=1
while test $it -le 12
do 
    Mth=`expr $Mth + $loop`
    file="DataFile"$Mth".bin"
    ./fort_exe ${Yr} ${nt} ${it} 

# Increment loop
it=`expr $it + 1`
done

このスクリプトは、do ループ内の 12 個のファイルを fortran プログラムに渡すために使用されます。Fortran プログラムでは、シェル スクリプトから渡されたバイナリ ファイルを読み取り、連続したファイルから読み取られたすべてのデータを 1 つのファイルにコンパイルする 2 番目のファイルを書き込もうとしています。

 !Open binary file passed from shell script
 open(1,file='Datafile'//TRIM{Mth)//.bin',action='read',form='unformatted',access='direct', &
 recl=4*x*y, status='old')

! Open write file for t 1. The status is different in t 1 and t > 1 so I open it twice: I guess there is a more elegant way to do this...
 open(2,file='Newfile.bin',action='write',form='unformatted', &
 access='stream', position='append', status='replace')
 irec = 0

 do t = 1, nt
 ! Read input file
  irec = irec + 1
  read(1,rec=irec) val(:,:)

 ! write output file
 irecW= irec + (imonth-1)*nt

 if ( t .eq. 1) write(2,pos=irecW) val(:,:)

 ! Close file after t = 1, update the status to old and reopen. 
 if ( t .eq. 2) then
    close (2)
    open(2,file='Newfile.bin',action='write',form='unformatted', &
         access='stream', position='append',status='old')
  endif

  if ( t .ge. 2) write(2,pos=irecW) val(:,:)

  enddo

最初のファイルからバイナリデータを問題なく読み取ることができますが、最初のプログラムで書き込んだファイルからバイナリデータを別のプログラムから読み取ろうとすると、

 open(1,file='Newfile.bin',action='read',form='unformatted', &
 access='stream', status='old')

 irec=0
 do t = 1, nt
! Read input file
     irec = irec + 1
     read(1,pos=irec) val(:,:)
     write(*,*) val(:,:)
 enddo

val(:,:) はゼロのリストに他なりません。これは、私が position='append' を使用できる唯一の方法であると信じている access=stream を使用するのは初めてです。gfortran と ifort でコンパイルしようとしましたが、エラー メッセージは表示されません。

なぜこれが起こっているのか誰にも分かりますか?

4

2 に答える 2

1

まず、出力ファイルを閉じて再度開く必要はないと思います。status指定子は、それが出現するステートメントにのみ関連しますopen。同じ名前の新しいファイルを開く前に、その時点で存在する場合replaceは削除します。Newfile.binステータスは暗黙的に に変更されますがold、これはファイルに対して行われた操作には影響しません。

ただし、Fortran コードは 12 回実行したことを認識していないため、ファイルが最初の 1 回だけ置き換えられ、oldその後は開かれることを確認する方法が必要です。それ以外の場合はNewfile.bin、最後に処理されたファイルからの情報のみが含まれます。

間違った値の読み取りに関しては、これは、直接アクセス (レコード長を選択できる場合) とストリーム アクセス (選択できない場合) の違いが原因で発生する可能性が最も高いです。ストリーム アクセスでは、データは一連の「ファイル ストレージ ユニット」として格納されます。それらのサイズは一般にコンパイラに依存しますが、モジュールを通じて;iso_fortran_envとして利用できます。file_storage_size通常は 8 ビットです。pos =これは、通常、各エントリが複数のストレージ ユニットを占有することを意味するため、指定子を使用した読み取りまたは書き込みが間違ったストレージ ユニットにアクセスしないように注意する必要があります。


編集:
ストリームアクセスを使用したコードの書き込みと読み取りの例:

program stream
  use, intrinsic :: iso_fortran_env
  implicit none

  integer :: i, offset
  real(real32), dimension(4,6) :: val, nval

  open(unit=2, file='Newfile.bin', action='readwrite', form='unformatted', &
     access='stream', status='replace')

  do i = 1,2
    call random_number(val)
    write(2) val
  enddo

  ! The file now contains two sequences of 24 reals, each element of which
  ! occupies the following number of storage units:
  offset = storage_size(val) / file_storage_size

  ! Retrieve the second sequence and compare:
  read(2, pos = 1 + offset*size(val)) nval
  print*, all(nval == val)

  close(2)
end program

trueが画面に出力されます。

また、データをファイルに書き込む際に a を厳密に指定する必要はないことにも注意しposてください。これは、ファイルが最後に読み書きされたレコードを超えて自動的に配置されるためです。


とはいえ、非連続的な方法でデータにアクセスする必要がある場合は、直接アクセスまたはストリーム アクセスが最も有益です。recl入力ファイルを 1 つに結合するだけでよい場合は、およびも指定できるシーケンシャル アクセスで出力ファイルを書き込む方が簡単な場合がありますposition = 'append'

于 2013-01-15T17:33:15.657 に答える
0

inquire次のステートメントを使用して、標準の Fortran でファイルの存在を確認できます。

logical :: exist

inquire(file="test.dat", exist=exist)
if (exist) then
  print *, "File test.dat exists"
else
  print *, "File test.dat does not exist"
end if

または、 libc のようなファイル操作ルーチンを提供するmodFileSysライブラリを参照することもできます。

追加とストリームについて: ファイルの追加は、「従来の」レコードベースの fortran ファイルを使用する場合にも可能です。そのためにストリームを使用する必要はありません。

于 2013-01-16T12:31:52.770 に答える