Запись больших бинарных файлов на Фортране с access=stream

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

SUBROUTINE write_field(d,m,outfile)

    IMPLICIT NONE    
    REAL, INTENT(IN) :: d(m,m,m)
    INTEGER, INTENT(IN) :: m
    CHARACTER(len=256), INTENT(IN) :: outfile

    OPEN(7,file=outfile,form='unformatted',access='stream')
    WRITE(7) d
    CLOSE(7)

END SUBROUTINE write_field

Мое понимание опции access=stream заключалось в том, что это подавит стандартный верхний и нижний колонтитулы, которые поставляются с двоичным файлом Fortran (см. Неформатированный формат файла Fortran).

Если я пишу файл с m=512, то я ожидаю, что файл должен быть 4 x 512^3 bytes = 536870912 bytes ~ 513 Mb, однако на самом деле они на 8 байтов длиннее этого, начиная с 536870920 bytes. Я предполагаю, что эти дополнительные байты представляют собой 4-байтовые верхний и нижний колонтитулы, которые я хотел подавить, используя access='stream'.

Ситуация становится для меня запутанной, если я пишу файл с m=1024, тогда я ожидаю, что файл должен быть 4 x 1024^3 bytes = 4294967296 ~ 4.1 Gb, однако на самом деле они на 24 (!) байта длиннее этого, входя в 4294967320 bytes. Я не понимаю, зачем здесь 24 лишних байта, которые вроде бы соответствуют 6(!) шапкам или футерам.

Мои вопросы:

(a) Можно ли заставить Fortran написать двоичный файл без верхних и нижних колонтитулов?

(б) Если ответ на (а) «нет», могу ли я гарантировать, что больший двоичный файл имеет ту же структуру верхнего и нижнего колонтитула, что и меньший двоичный файл?

(c) Если ответы на (a) и (b) оба «нет», то как мне понять, где в файле находятся эти дополнительные верхние и нижние колонтитулы.

Я использую ifort (версия 14.0.2) и пишу бинарные файлы на небольшом кластере Linux.

ОБНОВЛЕНИЕ: при запуске того же кода с OSx и компиляции с gfortran 7.3.0 двоичные файлы получаются с ожидаемыми размерами, так как они всегда 4 x m^3 bytes, даже если m=1024. Таким образом, эта проблема, похоже, связана со старым компилятором.

ОБНОВЛЕНИЕ: На самом деле проблема присутствует только при использовании ifort 14.0.2. Я обновил текст, чтобы отразить это.


person Mead    schedule 04.05.2018    source источник
comment
Работает ли это так, как ожидалось, для меньших значений m?   -  person ptb    schedule 04.05.2018
comment
@ptb Я думаю, что размер файла для меньших значений m всегда равен m^3+8, а затем он переключается на m^3+24 где-то между m=512 и m=1024.   -  person Mead    schedule 04.05.2018
comment
@VladimirF Я не знаю, где именно лишние байты. Я не пробовал писать аналогичный файл с C.   -  person Mead    schedule 04.05.2018
comment
Просто из любопытства, что произойдет, если вы добавите status='replace' к команде open и удалите form   -  person ptb    schedule 04.05.2018
comment
@VladimirF Возможно, это более актуально: stackoverflow.com/questions/15608421/ Я не могу не думать, что это как-то связано с ограничением ~ 2 ГБ для двоичных файлов Fortran.   -  person Mead    schedule 04.05.2018
comment
Первое, что я бы сделал, это написал бы очень маленький файл и посмотрел бы его в шестнадцатеричном редакторе.   -  person agentp    schedule 04.05.2018
comment
проблема большого файла спорна, так как при потоковом доступе нет маркеров записи.   -  person agentp    schedule 04.05.2018
comment
Боюсь, я не могу воспроизвести это. Я получаю именно то количество байтов, которое ожидается для m = 512, используя gfortran 7.3.1.   -  person ptb    schedule 04.05.2018
comment
@ptb У меня то же самое с 7.3.0. Смотрите мое обновление. Я предполагаю, что эта проблема связана со старой версией gfortran (и ifort), которую я использовал.   -  person Mead    schedule 04.05.2018
comment
В некоторых старых версиях ifort были ошибки в потоке, неформатированные записи больших объемов данных. Попробуйте более новую версию.   -  person Steve Lionel    schedule 06.05.2018
comment
Если это подтвержденная ошибка, кто-то должен опубликовать это как ответ   -  person agentp    schedule 07.05.2018
comment
вы смотрели на вас выходной файл? Дополнительные байты могут быть просто размером файла, указанным в строке 1. Просто предложение.   -  person Natsfan    schedule 09.05.2018


Ответы (1)


Эта проблема решается добавлением status='replace' в команду Fortran open. Это не связано с компилятором.

С access='stream' и без status='replace' старый бинарный файл не заменяется автоматически новым бинарным файлом, а просто перезаписывается до определенного момента (https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/676047). Это приводит к тому, что в старом двоичном файле просто заменяются байты до размера нового двоичного файла, при этом любые дополнительные байты и размер файла остаются неизменными. Это проблема, если новый размер файла меньше, чем старый размер файла. Проблему трудно диагностировать, поскольку временная метка файла обновляется, поэтому при запросе с использованием ls -l файл выглядит как новый.

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

PROGRAM write_binary_test_minimal

    IMPLICIT NONE
    REAL :: a

    a=1.

    OPEN(7,file='test',form='unformatted')
    WRITE(7) a
    CLOSE(7)

    OPEN(7,file='test',form='unformatted',access='stream')
    WRITE(7) a
    CLOSE(7)

END PROGRAM write_binary_test_minimal

Первый write генерирует файл "test" размером 8 + 4 = 12 байт. Где 8 — это стандартный бинарный заголовок и нижний колонтитул Fortran, а 4 — размер a в байтах. Во втором операторе write, несмотря на то, что было установлено access='stream', перезаписываются только первые 4 байт ранее сгенерированного «теста», оставляя файл размером 12 байт! Решение этой проблемы состоит в том, чтобы изменить второй оператор записи на

OPEN(7,file='test',form='unformatted',access='stream',status='replace')

с явным status='replace', чтобы убедиться, что старый файл заменен.

person Mead    schedule 28.05.2018
comment
Если я запускаю код, как я написал его выше, скомпилированный с помощью gfortran 8.1.0, я создаю файл размером 12 байт, который не заменяется вторым оператором записи. Я думаю, что это не просто старая версия ifort. - person Mead; 29.05.2018
comment
Извините, да, я не понял, что вы эффективно открываете файл для чтения и записи, и что для последовательного файла этого не происходит, но в потоковом файле вы можете читать и писать в любой позиции с помощью pos=, поэтому у вас не может быть остальных удаляются автоматически. - person Vladimir F; 29.05.2018