2015-05-11 5 views
0

у меня есть некоторые данные байт (скажем, для изображения):Расщепление список байт в список dicts

00 19 01 21 09 0f 01 15 .. FF

я разобрать его и хранить его в виде списка байт:

[b'\x00', b'\x19', b'\x01', b'\x21', b'\x09', b'\x0f', b'\x01', b'\x15', ...]

Эти значения RGBA (прямой порядок байтов, 2 байта), что мне нужно, чтобы разобрать в формате Словаре следующим образом:

[{'red':0x0019, 'green':0x2101, 'blue':0x0f09, 'alpha':0x1501}, {'red':...},...]

Примечание: данные изображения заканчиваются, как только мы достигнем 0xff. Значения могут храниться в шестнадцатеричном или десятичном значении, не имеет значения, если это согласовано.

Моя попытка:

# our dict keys 
keys = ['red', 'green', 'blue', 'alpha'] 

# first, grab all bytes until we hit 0xff 
img = list(takewhile(lambda x: x != b'\xFF', bitstream)) 

# traverse img 2 bytes at a time and join them 
rgba = [] 
for i,j in zip(img[0::2],img[1::2]): 
    rgba.append(b''.join([j,i]) # j first since byteorder is 'little' 

До сих пор он будет выводить [0x0019, 0x2101, 0x0f09, ...]

Теперь я застрял на том, как создать список dicts "pythonically". Я могу просто использовать цикл for и поп 4 элемента из списка за раз, но на самом деле это не использует возможности Python. Любой совет?

Примечание: это только пример, моими ключами могут быть любые (не связанные с изображениями). Также обратите внимание на любые проблемы с len(img) % len(keys) != 0.

ответ

3

Во-первых, использовать StringIO создать файл-подобный объект из битового потока для облегчения захвата 8-байтовых Куски один за один раз. Затем используйте struct.unpack для преобразования каждого 8-байтового фрагмента в кортеж из 4 целых чисел, который мы запишем с кортежем ключей, чтобы создать список, который можно передать непосредственно на dict. Все это обернуто в понимании списка, чтобы создать rgba за один проход.

(я использую functools.partial и itertools.imap улучшить readabililty.)

import StringIO 
import re 
from itertools import imap 
from functools import partial 

keys = ("red", "green", "blue", "alpha") 
# Create an object we can read from 
str_iter = StringIO.StringIO(re.sub("\xff.*", "", bitstream)) 
# A callable which reads 8 bytes at a time from str_iter 
read_8_bytes = partial(str_iter.read, 8) 
# Convert an 8-byte string into a tuple of 4 integer values 
unpack_rgba = partial(struct.unpack, "<HHHH") 
# An iterable of 8-byte strings 
chunk_iter = iter(read_8_bytes, '') 
# Map unpack_rgba over the iterator to get an iterator of 4-tuples, 
# then zip each 4-tuple with the key tuple to create the desired dict 
rgba = [dict(zip(keys, rgba_values)) 
     for rgba_values in imap(unpack_rgba, chunk_iter)] 

(Если вы получаете двоичные данные с чем-то вроде

with open('somefile', 'rb') as fh: 
    bitstream = fh.read() 

, то вы можете использовать файл итератор вместо str_iter, так что вы только читаете байты из файла по мере необходимости, а не все сразу.)

+0

Просто тангенциальную вопрос: Я собираюсь делать много байт пререкания с Python, и я хочу быть как я могу, любые предложения или библиотеки ресурсов, на которые я должен смотреть? –

+1

Могут быть библиотеки, которые реализуют схему, описанную выше, но вы можете пойти довольно далеко, просто используя то, что находится в стандартной библиотеке. Ключевым моментом здесь является использование двухфакторной формы 'item' для итерации по вашему файлу в кусках фиксированного размера, подходящих для распаковки с помощью' struct', а затем с использованием стандартных методов 'itertools' (или явных для циклов, если вы 't как функциональное программирование :)) для обработки результирующего потока. – chepner

1

Может быть, вместо того, чтобы

rgba = [] 
for i,j in zip(img[0::2],img[1::2]): 
    rgba.append(b''.join([j,i]) # j first since byteorder is 'little' 

Вы можете упростить его

rgba = [b''.join([j,i]) for i,j in zip(img[0::2], img[1::2])] 

Теперь вам нужно chunkify свой список, так что вы можете, возможно, брать рецепт от this link, то получим:

dict_list = [dict(zip(keys, chunk)) for chunk in chunks(rgba, 4)] 

eg

>>> keys = ['red', 'green', 'blue', 'alpha'] 
>>> test = [b'\x0019', b'\x2101', b'\x0f09', b'\x1501'] 
>>> dict(zip(keys, test)) 
{'blue': '\x0f09', 'alpha': '\x1501', 'green': '!01', 'red': '\x0019'} 
1

Не вдаваясь слишком фантазии, вы можете сделать это очень эффективно, как это:

try: 
    from itertools import izip 
except ImportError: # Python 3 
    izip = zip 

def grouper(n, iterable): 
    "s -> (s0,s1,...sn-1), (sn,sn+1,...s2n-1), (s2n,s2n+1,...s3n-1), ..." 
    return izip(*[iter(iterable)]*n) 

img = [b'\x00', b'\x19', b'\x01', b'\x21', b'\x09', b'\x0f', b'\x01', b'\x15', 
     b'\x01', b'\x1a', b'\x02', b'\x22', b'\x0a', b'\x10', b'\x02', b'\x16', 
     b'\xff'] 

keys = ['red', 'green', 'blue', 'alpha'] 
list_of_dicts = [dict(izip(keys, group)) 
        for group in grouper(4, (j+i for i,j in grouper(2, img)))] 

for value in list_of_dicts: 
    print(value) 
Смежные вопросы