Импорт неформатированного двоичного кода на Фортране - PullRequest
0 голосов
/ 09 июля 2019

У меня есть неформатированный двоичный файл, созданный с помощью компилятора Compaq Visual Fortran (с прямым порядком байтов).

Вот что говорит о нем небольшая часть документации:

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

  1. 8-символьное ключевое слово, которое идентифицирует данные в блоке.
  2. 4-байтовое целое число со знаком, определяющееколичество элементов в блоке.
  3. 4-символьное ключевое слово, определяющее тип данных.(INTE, REAL, LOGI, DOUB или CHAR) Элементы заголовка считываются как одна запись.Данные следуют за дескриптором новой записи.Числовые массивы разбиты на блоки по 1000 единиц.Физический размер записи совпадает с размером блока.

Additional keyword info:

Попытки чтения таких данных

module modbin
type rectype
    character(len=8)::key
    integer::data_count
    character(len=4)::data_type
    logical::is_int
    integer, allocatable:: idata(:)
    real(kind=8), allocatable::rdata(:)
end type
contains
subroutine rec_read(in_file, out_rec)
    integer, intent(in):: in_file
    type (rectype), intent(inout):: out_rec
    !
    ! You need to play around with this figure.  It may not be
    ! entirely accurate - 1000 seems to work, 1024 does not
    integer, parameter:: bsize = 1000
    integer:: bb, ii, iimax

    ! read the header
    out_rec%data_count = 0
    out_rec%data_type = '    '
    read(in_file, end = 20) out_rec%key, out_rec%data_count, 
out_rec%data_type
    ! what type is it?
    select case (out_rec%data_type)
    case ('INTE')
        out_rec%is_int = .true.
        allocate(out_rec%idata(out_rec%data_count))

    case ('DOUB')
        out_rec%is_int = .false.
        allocate(out_rec%rdata(out_rec%data_count))
    end select

    ! read the data in blocks of bsize
    bb = 1
    do while (bb .lt. out_rec%data_count)
        iimax = bb + bsize - 1
        if (iimax .gt. out_rec%data_count) iimax = out_rec%data_count
        if (out_rec%is_int) then
            read(in_file) (out_rec%idata(ii), ii = bb, iimax)
        else
            read(in_file) (out_rec%rdata(ii), ii = bb, iimax)
        end if
        bb = iimax + 1
    end do
20      continue
end subroutine rec_read

subroutine rec_print(in_recnum, in_rec)
    integer, intent(in):: in_recnum
    type (rectype), intent(in):: in_rec
    print *, in_recnum, in_rec%key, in_rec%data_count, in_rec%data_type
    ! print out data
    open(unit=12, file='reader.data' , status='old')
 write(12,*)key
 !write(*,'(i5')GEOMINDX
 !write(*,'(i5')ID_BEG
 !write(*,'(i5')ID_END
 !write(*,'(i5')ID_CELL
 !write(*,'(i5')TIME_BEG
 !write(*,'(i5')SWAT
 !format('i5')
      !end do
    close(12)

end subroutine rec_print
end module modbin

program main
use modbin
integer, parameter:: infile=11
! fixed size for now - should really be allocatable
integer, parameter:: rrmax = 500
type (rectype):: rec(rrmax)
integer:: rr, rlast

open(unit=infile, file='TEST1603.SLN0001', form='UNFORMATTED', 
status='OLD', convert='BIG_ENDIAN')
rlast = 0
do rr = 1, rrmax
    call rec_read(infile, rec(rr))
    if (rec(rr)%data_type .eq. '    ') exit
    rlast = rr
    call rec_print(rr, rec(rr))
end do
close(infile)
end program main

Этот код компилируется и работает гладкопоказывает

this

и не выдает ошибок, но это записано в выходном файле

this is what is written in the output file

не показывает никаких полезных числовых значений

Файл, о котором идет речь, доступен здесь

И правильный оператор WRITE должен создать такой файл здесь

Isмой ЗАПИСЬ, чтобы вывести этот тип файла неправильно?и если да, то как лучше?спасибо

1 Ответ

0 голосов
/ 13 июля 2019

В приведенных выше комментариях вы пытаетесь указать на одну из (как минимум) двух проблем в вашем коде.В подпрограмме rec_print у вас есть write(12,*)key, где вы хотели написать write(12,*)in_rec%key (по крайней мере, я думаю, это то, что вы хотели.)

Другая обнаруженная мною проблема заключается в том, что rec_print открывает reader.data с status='old' и затем закрывает его после записи ключа.(Использование old здесь предполагает, что файл уже существует.) Каждый раз, когда вызывается rec_print, файл открывается, первая запись перезаписывается, а файл закрывается.Одним из решений этой проблемы было бы использование status='unknown'. position='append', хотя было бы более эффективно открыть файл один раз в основной программе и просто позволить подпрограмме записать в него.

Если я внесу эти изменения, я получув файле данных:

INTEHEAD GEOMETRY GEOMINDX ID_BEG<br> ID_END<br> ID_CELL TIME_BEG SWAT

Дополнительный комментарий о CONVERT = и производных типах: это не влияет на вашу программу, но есть различия в компиляторе относительно способа чтения производногозапись типа с CONVERT = обрабатывается.Я думаю, что gfortran конвертирует каждый компонент в соответствии с его типом, но я знаю, что Intel Fortran не конвертирует операции чтения (или записи) целого производного типа.Вы читаете отдельные компоненты, которые работают в обоих компиляторах, так что это нормально, но я подумал, что стоит упомянуть.

Если вам интересно, почему Intel Fortran делает это таким образом, это из-за VAX FORTRAN (где CONVERT = пришел) наследие со СТРУКТУРОЙ / ЗАПИСЬЮ и возможным использованием UNION / MAP (недоступно в стандартном Fortran).С объединениями нет способа узнать, как конкретный компонент должен быть преобразован, поэтому он просто передает байты.Я предложил команде Intel это смягчить, если бы не было UNION, но я уверен, что это очень низкий приоритет.

...