Чтение записи 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
так что ваш читает правильно.
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
для вашего выбора.