Чтение записи fortran прямого доступа к неформатированным файлам с различными компиляторами

у меня есть раздел в программе, который пишет двоичный файл прямого доступа следующим образом:

open (53, file=filename, form='unformatted', status='unknown',
& access='direct',action='write',recl=320*385*8)
write (53,rec=1) ulat
write (53,rec=2) ulng
close(53)

эта программа компилируется с ifort. Однако я не могу восстановить данные правильно, если я прочитал файл данных из другой программы, скомпилированной с gfortran. Если программа чтения данных также компилируется в ifort, то я могу правильно восстановить данные. Вот код, считывающий файл данных:

OPEN(53, FILE=fname, form="unformatted", status="unknown", access="direct", action="read", recl=320*385*8)
READ(53,REC=2) DAT

Я не понимаю, почему это происходит? Я могу прочитайте первую запись правильно с обоими компиляторами, это вторая запись, которую я не могу восстановить должным образом, если я смешиваю компиляторы.

1 ответов


Ifort и gfortran по умолчанию не используют одинаковый размер блока для длины записи. В ifort, стоимостью recl в своем open оператор находится в 4-байтовых блоках, поэтому ваша длина записи составляет не 985,600 байт, а 3,942,400 байт. Это означает, что записи записываются с интервалом 3,9 миллиона байт.

gfortran использует recl размер блока 1 байт, а длина записи-985,600 байтов. Когда вы читаете первую запись, все работает, но когда вы читаете вторая запись вы смотрите на 985,600 байт в файл, но данные в 3,942,400 байт в файл. Это также означает, что вы тратите тонну данных в файле, так как вы используете только 1/4 его размера.

есть несколько способов исправить это:

  • в ifort укажите recl в 4-байтовых блоках, например 320*385*2 вместо *8
  • в ifort используйте флаг компиляции -assume byterecl иметь recl значения, интерпретируемые как байты.
  • In gfortran компенсировать размер и использовать recl=320*385*32 так что ваш читает правильно.
recl размер блока. Вы можете использовать inquire чтобы выяснить recl массива. Например:
real(kind=wp), allocatable, dimension(:,:) :: recltest
integer :: reclen
allocate(recltest(320,385))
inquire(iolength=reclen) recltest
deallocate(recltest)
...
open (53, file=filename, form='unformatted', status='unknown',
& access='direct',action='write',recl=reclen)
...
OPEN(53, FILE=fname, form="unformatted", status="unknown", &
access="direct", action="read", recl=reclen)

это reclen к значению, необходимому для хранения 320x385 массив на основе базового блока компиляторов that для длины записи. Если вы используете это, когда запись и чтение кода будут работать с обоими компиляторами без использования флагов времени компиляции в ifort или компенсировать жестко закодированные различия recl между компиляторами.


наглядный пример

Testcase 1

program test
  use iso_fortran_env
  implicit none

  integer(kind=int64), dimension(5) :: array
  integer :: io_output, reclen, i
  reclen = 5*8 ! 5 elements of 8 byte integers.

  open(newunit=io_output, file='output', form='unformatted', status='new', &
       access='direct', action='write', recl=reclen)

  array = [(i,i=1,5)]  
  write (io_output, rec=1) array
  array = [(i,i=101,105)]
  write (io_output, rec=2) array
  array = [(i,i=1001,1005)]
  write (io_output, rec=3) array

  close(io_output)
end program test

эта программа записывает массив из 5 8-байтовых целых чисел 3 раза в файл в записях 1,2 и 3. Массив составляет 5*8 байт, и я жестко закодировал это число как значение recl.

Testcase 1 с gfortran 5.2

я скомпилировал этот testcase с командной строкой:

gfortran -o write-gfortran write.f90

это создает выходной файл (интерпретируется с od -A d -t d8):

0000000                    1                    2
0000016                    3                    4
0000032                    5                  101
0000048                  102                  103
0000064                  104                  105
0000080                 1001                 1002
0000096                 1003                 1004
0000112                 1005
0000120

массивы из 5 8-bye элементов упакованы смежно в файл и запись номер 2 (101 ... 105) начинается там, где мы ожидали бы его со смещением 40, которое является значением recl в файле 5*8.

Testcase 1 с ifort 16

это составлено аналогично:

ifort -o write-ifort write.f90

и это, для точного тот же код, создает выходной файл (интерпретируется с od -A d -t d8):

0000000                    1                    2
0000016                    3                    4
0000032                    5                    0
0000048                    0                    0
*
0000160                  101                  102
0000176                  103                  104
0000192                  105                    0
0000208                    0                    0
*
0000320                 1001                 1002
0000336                 1003                 1004
0000352                 1005                    0
0000368                    0                    0
*
0000480

данные все есть, но файл полон 0-значных элементов. Строки, начинающиеся с * укажите, что каждая строка между смещениями равна 0. Запись номер 2 начинается со смещения 160 вместо 40. Обратите внимание, что 160-это 40 * 4, где 40 - наш указанный recl 5*8. По умолчанию ifort использует 4-байтовые блоки, поэтому recl 40 означает размер физической записи 160 байт.

если код скомпилированные с gfortran должны были прочитать это, записи 2,3 и 4 будут содержать все 0 элементов, а чтение записи 5 будет правильно читать массив, написанный как запись 2 ifort. Альтернативой тому, чтобы gfortran читал запись 2, где она находится в файле, было бы использование recl=160 (4*5*4) таким образом, размер физической записи соответствует тому, что было написано ifort.

еще одно последствие этого-потраченное впустую пространство. Чрезмерное указание recl означает, что вы используете 4 раза необходимое дисковое пространство для храните свои записи.

Testcase 1 с ifort 16 и -assume byterecl

это было составлено как:

ifort -assume byterecl -o write-ifort write.f90

и создает выходной файл:

0000000                    1                    2
0000016                    3                    4
0000032                    5                  101
0000048                  102                  103
0000064                  104                  105
0000080                 1001                 1002
0000096                 1003                 1004
0000112                 1005
0000120

это создает файл, как ожидалось. Аргумент командной строки -assume byterecl говорит ifort интерпретировать любой recl значения как байты, а не двойные слова (4-байтовые блоки). Это приведет к записи и чтению, которые соответствуют коду, скомпилированному с gfortran.

Testcase 2

program test
  use iso_fortran_env
  implicit none

  integer(kind=int64), dimension(5) :: array
  integer :: io_output, reclen, i
  inquire(iolength=reclen) array
  print *,'Using recl=',reclen

  open(newunit=io_output, file='output', form='unformatted', status='new', &
       access='direct', action='write', recl=reclen)

  array = [(i,i=1,5)]  
  write (io_output, rec=1) array
  array = [(i,i=101,105)]
  write (io_output, rec=2) array
  array = [(i,i=1001,1005)]
  write (io_output, rec=3) array

  close(io_output)
end program test

единственная разница в этом тестовом наборе заключается в том, что я запрашиваю правильный recl для представления моего 40-байтового массива (5 8-байтовых целых чисел).

выход

gfortran 5.2:

 Using recl=          40

ifort 16, без вариантов:

 Using recl=          10

ifort 16, -assume byterecl:

 Using recl=          40

мы видим, что для 1-байтовых блоков, используемых gfortran и ifort с byterecl предположение, что recl 40, что равно нашему 40-байтовому массиву. Мы также видим, что по умолчанию ifort использует recl 10, что означает 10 4-байтовых блоков или 10 двойных слов, оба из которых означают 40 байтов. Все три из этих тестовых наборов производят идентичный вывод файла, и чтение / запись из любого компилятора будет функционировать должным образом.

резюме

чтобы иметь записи на основе, неформатированные, прямые данные быть переносимым между ifort и gfortran самый простой вариант-просто добавить -assume byterecl к флагам, используемым ifort. Тебе действительно стоило это сделать. уже Поскольку вы указываете длину записи в байтах, это будет простое изменение, которое, вероятно, не имеет для вас последствий.

другой альтернативой является не беспокоиться о опции и использовать inquire intrinsic для запроса iolength для вашего выбора.