Чтение двоичных данных в 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_rows
xn_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, например (анализируется из вашего заголовка)
объяснение об ошибке .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'])