昨天我不得不解析一个非常简单的二进制数据文件 - 规则是,连续查找两个字节都是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)
您可以为状态指定常量名称,而不是使用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.
我见过用Python实现FSM的最酷方式必须是通过生成器和协同程序.有关示例,请参阅此可爱的Python帖子.Eli Bendersky也对这个主题有很好的对待.
如果协同程序不是熟悉的领域,David Beazley的关于协同程序和并发的好奇课程是一个出色的介绍.