Что Pythonic путь состоит в том, чтобы реализовать простой FSM?

Вчера я должен был проанализировать очень простой файл двоичных данных - правило, ищите два байта подряд, которые являются оба 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"
13
задан Vicky 26 May 2010 в 19:38
поделиться

7 ответов

Вы можете давать состояниям константные имена вместо 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.

7
ответ дан 1 December 2019 в 22:56
поделиться

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

1
ответ дан 1 December 2019 в 22:56
поделиться

Вы можете использовать регулярные выражения. Что-то вроде этого кода найдет первый блок данных. Тогда это всего лишь случай начала следующего поиска после предыдущего совпадения.

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]
1
ответ дан 1 December 2019 в 22:56
поделиться

Я немного опасаюсь говорить кому-либо, что такое Pythonic, но все же скажу. Во-первых, имейте в виду, что в python функции - это просто объекты. Переходы могут быть определены с помощью словаря, который имеет (input, current_state) в качестве ключа и кортеж (next_state, action) в качестве значения. Действие - это просто функция, которая делает все необходимое для перехода от текущего состояния к следующему.

На сайте http://code.activestate.com/recipes/146262-finite-state-machine-fsm есть хороший пример, как это делается. Я не использовал его, но при беглом чтении кажется, что он охватывает все.

Похожий вопрос был задан/отвечен здесь пару месяцев назад: Python state-machine design. Возможно, вам также будет полезно ознакомиться с этими ответами.

3
ответ дан 1 December 2019 в 22:56
поделиться

Самый крутой способ реализации FSM в Python, который я видел, - это генераторы и корутины. Пример см. в этом посте Charming Python. У Илая Бендерски также есть отличное изложение темы.

Если корутины вам незнакомы, то книга Дэвида Бизли A Curious Course on Coroutines and Concurrency является отличным введением.

6
ответ дан 1 December 2019 в 22:56
поделиться

Я думаю, что ваше решение выглядит нормально, за исключением того, что вы должны заменить count = count - 1 на count -= 1.

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

1
ответ дан 1 December 2019 в 22:56
поделиться

Советую ознакомиться с главой 4 книги "Обработка текста в Python" Дэвида Мерца. Он реализует класс машины состояния в Python, который очень элегантен.

1
ответ дан 1 December 2019 в 22:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: