Чтение двоичного формата файлов прямого доступа в Python - PullRequest
0 голосов
/ 26 сентября 2018

Справочная информация:

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

        parameter(nx=720, ny=360, nday=365)
c 
        dimension tmax(nx,ny,nday),nmax(nx,ny,nday)
        dimension tmin(nx,ny,nday),nmin(nx,ny,nday)
c 
        open(10,
     &file='FILE',
     &access='direct',recl=nx*ny*4)
c
        do k=1,nday
        read(10,rec=(k-1)*4+1)((tmax(i,j,k),i=1,nx),j=1,ny) 
        read(10,rec=(k-1)*4+2)((nmax(i,j,k),i=1,nx),j=1,ny) 
        read(10,rec=(k-1)*4+3)((tmin(i,j,k),i=1,nx),j=1,ny) 
        read(10,rec=(k-1)*4+4)((nmin(i,j,k),i=1,nx),j=1,ny) 
        end do

Сведения о файле:

options  little_endian
title global daily analysis (grid box mean, the grid shown is the center of the grid box)
undef -999.0
xdef 720 linear    0.25 0.50
ydef 360  linear -89.75 0.50
zdef 1 linear 1 1
tdef 365 linear 01jan2015 1dy
vars 4
tmax     1  00 daily maximum temperature (C)
nmax     1  00 number of reports for maximum temperature (C)
tmin     1  00 daily minimum temperature (C)
nmin     1  00 number of reports for minimum temperature (C)
ENDVARS

Попытки вРешение:

Я пытаюсь разобрать это в массив на python, используя следующий код (намеренно пропуская два атрибута):

with gzip.open("/FILE.gz", "rb") as infile:
     data = numpy.frombuffer(infile.read(), dtype=numpy.dtype('<f4'), count = -1)

while x <= len(data) / 4:
    tmax.append(data[(x-1)*4])
    tmin.append(data[(x-1)*4 + 2])
    x += 1

data_full = zip(tmax, tmin)

При тестировании некоторых записей данные не кажутсявыровнять некоторые примеры записей из файла при использовании Фортрана.Я также попробовал dtype=numpy.float32, но безуспешно.Кажется, будто я правильно читаю файл с точки зрения количества наблюдений.Я также использовал struct, прежде чем узнал, что файл был создан с помощью Fortran.Это не сработало

Здесь есть похожие вопросы, на некоторые из которых есть ответы, которые я безуспешно пытался адаптировать.

ОБНОВЛЕНИЕ

Я немного ближе после того, как опробовал этот код:

#Define numpy variables and empty arrays
nx = 720 #number of lon
ny = 360 #number of lat
nday = 0 #iterate up to 364 (or 365 for leap year)   
tmax = numpy.empty([0], dtype='<f', order='F')
tmin = numpy.empty([0], dtype='<f', order='F')

#Parse the data into numpy arrays, shifting records as the date increments
while nday < 365:
    tmax = numpy.append(tmax, data[(nx*ny)*nday:(nx*ny)*(nday + 1)].reshape((nx,ny), order='F'))
    tmin = numpy.append(tmin, data[(nx*ny)*(nday + 2):(nx*ny)*(nday + 3)].reshape((nx,ny), order='F'))
    nday += 1  

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

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

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

Ошибка в использовании дня для итерации по записям.Это не будет работать, так как он повторяется один раз за цикл, не выдвигая записи достаточно далеко.Следовательно, почему некоторые минуты были выше, чем максимумы.Новый код:

while nday < 365:
    tmax = numpy.append(tmax, data[(nx*ny)*rm:(nx*ny)*(rm + 1)].reshape((nx,ny), order='F'))
    rm = rm + 2
    tmin = numpy.append(tmin, data[(nx*ny)*rm:(nx*ny)*(rm + 1)].reshape((nx,ny), order='F'))
    rm = rm + 2
    nday += 1 

При этом для перемещения записей на нужную сумму использовался механизм записи (или, как я его называю, rm).Это было все, что нужно.

0 голосов
/ 27 сентября 2018

Хотя точный формат двоичных файлов на Фортране зависит от компилятора, во всех случаях мне известны файлы прямого доступа (файлы, открытые с access='direct', как в этом вопросе), не имеют маркеров между записями.Каждая запись имеет фиксированный размер, заданный спецификатором recl= в операторе OPEN.То есть запись N начинается со смещения (N - 1) * RECL байтов в файле.

Одна ошибка переносимости заключается в том, что единица измерения recl= выражается в file storage unit с.Для большинства компиляторов file storage unit указывает размер в 8-битных октетах (как рекомендовано в последних версиях стандарта Fortran), но для компилятора Intel Fortran recl= в единицах 32 бита;есть параметр командной строки -assume byterecl, который можно использовать для соответствия Intel Fortran большинству других компиляторов.

Таким образом, в приведенном здесь примере с использованием 8-разрядного file storage unit ваш recl будет составлять 1036800 байт.

Далее, глядя на код, можно предположить, что массивы имеют 4-байтовый тип (например, целое число или вещественное с одинарной точностью).Так что, если это реальная точность с одинарной точностью и файл был создан с прямым порядком байтов, то использованный вами тип dump <f4 кажется правильным.

Теперь вернемся к компилятору Intel FortranГоча, если файл был создан ifort без -assume byterecl, то данные, которые вы хотите, будут в первой четверти каждой записи, а остальные заполнены (все нули или, может быть, даже случайные данные?).Затем вам придется выполнить дополнительную гимнастику, чтобы извлечь правильные данные в python, а не в padding.Это должно быть легко проверить, проверив размер файла, nx * ny * 4 * nday *4 или nx * ny * 4 * nday * 4 * 4 байт?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...