Вчера я должен был проанализировать очень простой файл двоичных данных - правило, ищите два байта подряд, которые являются оба 0xAA, затем следующий байт будет байтом длины, затем пропустить 9 байтов и произвести данный объем данных оттуда. Повторитесь в конец файла.
Мое решение действительно работало и было очень быстро для соединения (даже при том, что я - программист C в глубине души, я все еще думаю, что это было более быстро для меня для записи этого в Python, чем это будет в C) - НО, это не ясно нисколько Pythonic, и это читает как программа C (и не очень хороший в этом!)
Каково было бы лучшее / больше подхода Pythonic к этому? Является простой FSM как это даже все еще правильным выбором в Python?
Мое решение:
#! /usr/bin/python
import sys
f = open(sys.argv[1], "rb")
state = 0
if f:
for byte in f.read():
a = ord(byte)
if state == 0:
if a == 0xAA:
state = 1
elif state == 1:
if a == 0xAA:
state = 2
else:
state = 0
elif state == 2:
count = a;
skip = 9
state = 3
elif state == 3:
skip = skip -1
if skip == 0:
state = 4
elif state == 4:
print "%02x" %a
count = count -1
if count == 0:
state = 0
print "\r\n"
Вы можете давать состояниям константные имена вместо 0, 1, 2 и т.д. для улучшения читабельности.
Вы можете использовать словарь для отображения (текущее_состояние, вход) -> (следующее_состояние)
, но это не позволит вам выполнять дополнительную обработку во время переходов. Если только вы не включите какую-нибудь "функцию перехода" для дополнительной обработки.
Или вы можете использовать не-FSM подход. Я думаю, это будет работать до тех пор, пока 0xAA 0xAA
появляется только тогда, когда указывает на "начало" (не появляется в данных).
with open(sys.argv[1], 'rb') as f:
contents = f.read()
for chunk in contents.split('\xaa\xaa')[1:]:
length = ord(chunk[0])
data = chunk[10:10+length]
print data
Если он появляется в данных, вы можете вместо этого использовать string.find('\xaa\xaa', start)
для сканирования строки, задавая аргумент start
, чтобы начать поиск с места окончания последнего блока данных. Повторяйте, пока не вернется -1.
Я думаю, что наиболее питонический способ будет похож на то, что предлагает FogleBird, но сопоставить (текущее состояние, ввод) с функцией, которая будет обрабатывать обработку и переход.
Вы можете использовать регулярные выражения. Что-то вроде этого кода найдет первый блок данных. Тогда это всего лишь случай начала следующего поиска после предыдущего совпадения.
find_header = re.compile('\xaa\xaa(.).{9}', re.DOTALL)
m = find_header.search(input_text)
if m:
length = chr(find_header.group(1))
data = input_text[m.end():m.end() + length]
Я немного опасаюсь говорить кому-либо, что такое Pythonic, но все же скажу. Во-первых, имейте в виду, что в python функции - это просто объекты. Переходы могут быть определены с помощью словаря, который имеет (input, current_state) в качестве ключа и кортеж (next_state, action) в качестве значения. Действие - это просто функция, которая делает все необходимое для перехода от текущего состояния к следующему.
На сайте http://code.activestate.com/recipes/146262-finite-state-machine-fsm есть хороший пример, как это делается. Я не использовал его, но при беглом чтении кажется, что он охватывает все.
Похожий вопрос был задан/отвечен здесь пару месяцев назад: Python state-machine design. Возможно, вам также будет полезно ознакомиться с этими ответами.
Самый крутой способ реализации FSM в Python, который я видел, - это генераторы и корутины. Пример см. в этом посте Charming Python. У Илая Бендерски также есть отличное изложение темы.
Если корутины вам незнакомы, то книга Дэвида Бизли A Curious Course on Coroutines and Concurrency является отличным введением.
Я думаю, что ваше решение выглядит нормально, за исключением того, что вы должны заменить count = count - 1
на count -= 1
.
Это один из тех случаев, когда причудливые кодеры придумывают способы отображения состояний dicts на callables, с небольшой функцией драйвера, но это не лучше, просто более причудливо, и использует более непонятные особенности языка.
Советую ознакомиться с главой 4 книги "Обработка текста в Python" Дэвида Мерца. Он реализует класс машины состояния в Python, который очень элегантен.