Чтение двоичных данных в python

во-первых, прежде чем этот вопрос будет помечен как дубликат, я знаю, что другие задавали подобные вопросы, но, похоже, нет четкого объяснения. Я пытаюсь прочитать двоичный файл в 2D-массив (хорошо документированный здесь http://nsidc.org/data/docs/daac/nsidc0051_gsfc_seaice.gd.html).

заголовок-это 300 байт массива.

до сих пор у меня;

import struct

with open("nt_197912_n07_v1.1_n.bin",mode='rb') as file:
    filecontent = file.read()

x = struct.unpack("iiii",filecontent[:300])

выдает ошибку длины строкового аргумента.

1 ответов


чтение данных (короткий ответ)

после того как вы определили размеры решетки (n_rowsxn_cols = 448x304) из вашего заголовка (см. Ниже), вы можете просто прочитать данные, используя numpy.frombuffer.

import numpy as np

#...

#Get data from Numpy buffer
dt = np.dtype(('>u1', (n_rows, n_cols)))
x = np.frombuffer(filecontent[300:], dt) #we know the data starts from idx 300 onwards

#Remove unnecessary dimension that numpy gave us
x = x[0,:,:]

на '>u1' задает формат данных, в данном случае без подписи целые числа размером 1 байт, которые имеют формат big-endian.

заговоре с matplotlib.pyplot

import matplotlib.pyplot as plt

#...

plt.imshow(x, extent=[0,3,-3,3], aspect="auto")
plt.show()

на extent= опция просто указывает значения оси, вы можете изменить их на lat / lon, например (анализируется из вашего заголовка)

Output

объяснение об ошибке .unpack ()

С документы на struct.unpack(fmt, string):

строка должна содержать ровно столько данных, сколько требуется для формата (len(string) должен быть равен calcsize(fmt))

вы можете определить размер указан в формате строка (fmt) по Символа Формат.

код fmt на struct.unpack("iiii",filecontent[:300]), определяет 4 типа int (вы также можете использовать 4i = iiii для простоты), каждый из которых имеет размер 4, требующий строки длиной 16.

строка (filecontent[:300]) имеет длину 300, в то время как ваш fmt запрашивает строку длины 16, отсюда и ошибка.

пример использования .unpack ()

в качестве примера прочтите предоставленный документ я извлек первые 21 * 6 байт, который имеет формат:

21-элементный массив 6-байтовых символьных строк, содержащих такую информацию, как характеристики полярной стереографической сетки

С:

x = struct.unpack("6s"*21, filecontent[:126])

это возвращает кортеж из 21 элементов. Обратите внимание на заполнение пробелов в некоторых элементах для удовлетворения 6-байтового требования.

>> print x
    # ('00255\x00', '  304\x00', '  448\x00', '1.799\x00', '39.43\x00', '45.00\x00', '558.4\x00', '154.0\x00', '234.0\x00', '
    # SMMR\x00', '07 cn\x00', '  336\x00', ' 0000\x00', ' 0034\x00', '  364\x00', ' 0000\x00', ' 0046\x00', ' 1979\x00', '  33
    # 6\x00', '  000\x00', '00250\x00')

Примечания:

  • первый аргумент fmt, "6s"*21 это строка с 6s повторяется 21 раз. Каждый формат-символ 6s представляет одну строку из 6 байт (см. ниже), это будет соответствовать требуемому формату, указанному в документ.
  • количество 126 на filecontent[:126] рассчитывается как 6*21 = 126.
  • обратите внимание, что для s (string) спецификатор, предыдущее число тут не означает повторить символ формата 6 раз (как это было бы обычно для других символов форматирования). Вместо, он определяет размер струны. s представляет собой 1-байтовую строку, в то время как 6s представляет 6-байтовая строка.

более обширное решение для чтения заголовка (длинное)

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

эта функция будет читать заголовок и хранить его в словаре, где структура задается из а

# user configparser for Python 3x
import ConfigParser

def read_header(data, config_file):
    """
    Read binary data specified by a INI file which specifies the structure
    """

    with open(config_file) as fd:

        #Init the config class
        conf = ConfigParser.ConfigParser()
        conf.readfp(fd)

        #preallocate dictionary to store data
        header = {}

        #Iterate over the key-value pairs under the
        #'Structure' section
        for key in conf.options('structure'):

            #determine the string properties
            start_idx, end_idx = [int(x) for x in conf.get('structure', key).split(',')]
            start_idx -= 1 #remember python is zero indexed!
            strLength = end_idx - start_idx

            #Get the data
            header[key] = struct.unpack("%is" % strLength, data[start_idx:end_idx])

            #Format the data
            header[key] = [x.strip() for x in header[key]]
            header[key] = [x.replace('\x00', '') for x in header[key]]

        #Unmap from list-type
        #use .items() for Python 3x
        header = {k:v[0] for k, v in header.iteritems()}

    return header

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

[structure]
missing_data: 1, 6
n_cols: 7, 12
n_rows: 13, 18
latitude_enclosed: 25, 30

эту функцию можно использовать следующим образом:

header = read_header(filecontent, 'headerStructure.ini')
n_cols = int(header['n_cols'])