2010-05-26 6 views
11

어제 매우 간단한 바이너리 데이터 파일을 구문 분석해야했습니다. 규칙은 0xAA 인 행에서 2 바이트를 찾은 후 다음 바이트가 길이 바이트가 될 것이고 9 바이트를 건너 뛰고 주어진 양을 출력합니다 거기에서 데이터. 파일 끝까지 반복하십시오.간단한 FSM을 구현하는 Pythonic 방법은 무엇입니까?

내 솔루션이 제대로 작동하고 결합하기가 매우 빠르다. (비록 마음에 든 C 프로그래머 임에도 불구하고 여전히 C로 작성했을 때보 다 파이썬에서 이것을 작성하는 것이 더 빠르다고 생각한다.) -하지만 , 분명히 Pythonic이 아니며 C 프로그램처럼 읽습니다. (매우 좋은 것은 아닙니다!)

더 나은/더 Pythonic 한 접근 방법은 무엇입니까? 파이썬에서 이처럼 간단한 FSM이 올바른 선택일까요?

내 솔루션 :

#! /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" 
+1

나에게 잘 보입니다. 그게 내가 쓰는 방법이야. –

+10

글쎄, 먼저 맥주 화산이 필요하고, 그럼 해적 복장이 필요해. 잠깐만, 여기서 어떤 FSM 얘기를하고 있니? –

+4

피곤해야합니다. FSM을 Flying Spaghetti Monster로 계속 읽습니다. –

답변

6

가독성을 높이기 위해 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 

대신 마지막 데이터 블록이 종료 위치를 찾고 시작합니다 start 인수를 설정, 문자열을 통해 스캔 string.find('\xaa\xaa', start)를 사용할 수 있습니다. -1이 될 때까지 반복하십시오.

+0

화려한 - 고마워요! 나는이 같은 방법이있을 거라는 것을 알았지 만 내 머리를 확실히 잡을 수 없었습니다. – Vicky

+0

Oh - 0xAA 0xAA가 유효한 데이터에 나타나지 않습니다. 유효한 데이터 덩어리 사이에 쓰레기로 나타날 수도 있습니다.이 경우 귀하의 솔루션이나 광산 (또는 다른 솔루션 인 AFAICT)이 처리 할 수 ​​없습니다. – Vicky

0

난 당신이 count -= 1count = count - 1를 교체해야합니다 제외 솔루션이 잘 보이는 것 같아요.

이것은 멋진 코드 쇼 오프가 작은 드라이버 기능으로 상태를 콜 가능으로 매핑하는 방법을 보여줄 것입니다.하지만 더 좋고, 더 애호가이며 더 모호한 언어를 사용합니다 풍모.

5

파이썬에서 FSM을 구현하는 가장 멋진 방법은 발전기와 코 루틴을 사용해야한다는 것입니다. 예를 들어 Charming Python post을 참조하십시오. Eli Bendersky도 an excellent treatment of the subject입니다.

코 루틴이 익숙하지 않은 지역 인 경우 David Beazley의 A Curious Course on Coroutines and Concurrency은 별의 소개입니다.

+0

와우! 고마워요. 저는 Beazley의 참고서를 읽기 시작했습니다. 그리고 저녁 시간이 대부분 걸리지 만 이미 가치가 있습니다. – Pierce

+0

놀랍습니다. 나는 무겁게 (OK, 전적으로) 긴급한 배경에서 왔고, 처음 읽을 때 내 마음을 완전히 불었다. –

+0

흥미 롭습니다 - 감사합니다! 아마 내 특정 사건에 대한 과잉인데, 그럼에도 불구하고 매우 냉담합니다. – Vicky

0

가장 평이한 방법은 FogleBird가 제안한 것과 비슷하지만 (현재 상태, 입력)에서 처리 및 전환을 처리 할 함수로 매핑하는 것입니다.

2

나는 Pythonic이 무엇인지 이야기하는 것에 대해 약간 불안하지만 여기서는 간다. 우선, 파이썬 함수는 단지 객체라는 것을 명심하십시오. 전이는 (input, current_state)를 키로하고 튜플 (next_state, action)을 값으로 갖는 사전으로 정의 할 수 있습니다. 액션은 현재 상태에서 다음 상태로 전환하는 데 필요한 모든 것을 수행하는 함수입니다.

http://code.activestate.com/recipes/146262-finite-state-machine-fsm에서이 작업을 수행하는 좋은 예가 있습니다.나는 그것을 사용하지 않았지만 빠른 읽기에서 모든 것을 다루는 것처럼 보입니다.

몇 달 전에 비슷한 질문을 던졌음/대답 : Python state-machine design. 이러한 응답을 유용하게 볼 수 있습니다.

0

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] 
관련 문제