什么是Pythonic实现简单FSM的方法?

Vic*_*cky 13 python 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"
Run Code Online (Sandbox Code Playgroud)

Fog*_*ird 7

您可以为状态指定常量名称,而不是使用0,1,2等,以提高可读性.

您可以使用字典进行映射(current_state, input) -> (next_state),但这并不能让您在转换期间进行任何其他处理.除非你包括一些"过渡功能",否则做额外的处理.

或者你可以做一个非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
Run Code Online (Sandbox Code Playgroud)

如果它确实出现在数据中,您可以改为使用string.find('\xaa\xaa', start)扫描字符串,将start参数设置为开始查看最后一个数据块结束的位置.重复直到它返回-1.


Han*_*Gay 6

我见过用Python实现FSM的最酷方式必须是通过生成器和协同程序.有关示例,请参阅此可爱的Python帖子.Eli Bendersky也对这个主题很好的对待.

如果协同程序不是熟悉的领域,David Beazley的关于协同程序和并发的好奇课程是一个出色的介绍.