質問
昨日、非常に単純なバイナリデータファイルを解析する必要がありました。ルールは、両方とも0xAAの2つのバイトを調べ、次のバイトは長さのバイトになり、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などを使用する代わりに、状態に一定の名前を与えることができます。
辞書を使用してマッピングできます (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
データに表示される場合は、代わりに使用できます string.find('\xaa\xaa', start)
文字列をスキャンするには、設定します start
最後のデータブロックが終了した場所を探し始めるという議論。 -1が戻るまで繰り返します。
他のヒント
PythonでFSMを実装するために私が見た最もクールな方法は、発電機とコルーチンを介して必要です。これを参照してください 魅力的なPython投稿 例として。エリ・ベンダースキーも持っています 被験者の優れた治療.
Coroutinesが馴染みのある領土でなければ、David Beazley's CoroutinesとConcurrencyに関する奇妙なコース 素晴らしい紹介です。
私は誰にでもパトニックとは何かを伝えることについて少し不安ですが、ここに行きます。まず、Python機能は単なるオブジェクトであることに注意してください。遷移は、(入力、current_state)をキーとして、タプル(next_state、action)を値として持つ辞書で定義できます。アクションは、現在の状態から次の状態に移行するために必要なことを行う機能です。
これを行うという見栄えの良い例があります http://code.activestate.com/recipes/146262-finite-state-machine-fsm. 。私はそれを使用していませんが、簡単に読むことから、それはすべてをカバーしているようです。
同様の質問が数ヶ月前にここで尋ねられました: Python State-Machine Design. 。これらの応答も有用であると思うかもしれません。
あなたが交換する必要があることを除いて、私はあなたのソリューションがうまく見えると思います count = count - 1
と count -= 1
.
これは、小さなドライバー機能を使用して、派手なコードショーオフが状態を呼び出すことをマッピングする方法を発揮する方法の1つですが、それはより良くなく、よりファンシーで、より曖昧な言語機能を使用します。
チェックアウトすることをお勧めします Pythonのテキスト処理の第4章 デビッド・マーツによる。彼は非常にエレガントなPythonのState Machineクラスを実装しています。
最もパトニックな方法は、Foglebirdが提案したものを好むが、(現在の状態、入力)から処理と遷移を処理する関数へのマッピングをすると思います。
Regexpsを使用できます。このコードのようなものは、データの最初のブロックを見つけます。その後、前の試合後から次の検索を開始するだけです。
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]