파일을 파싱하는 가장 좋은 방법


9

EDIFACTTRADACOMS 와 같은 유명한 파일 형식 중 일부에 대한 파서를 만드는 더 나은 솔루션을 찾으려고 합니다.

이러한 표준에 익숙하지 않은 경우 Wikipedia에서이 예제를 확인하십시오.

제품 가용성 요청에 응답하는 데 사용되는 EDIFACT 메시지의 예는 아래를 참조하십시오.

UNA:+.? '
UNB+IATB:1+6XPPC+LHPPC+940101:0950+1'
UNH+1+PAORES:93:1:IA'
MSG+1:45'
IFT+3+XYZCOMPANY AVAILABILITY'
ERC+A7V:1:AMD'
IFT+3+NO MORE FLIGHTS'
ODI'
TVL+240493:1000::1220+FRA+JFK+DL+400+C'
PDI++C:3+Y::3+F::1'
APD+714C:0:::6++++++6X'
TVL+240493:1740::2030+JFK+MIA+DL+081+C'
PDI++C:4'
APD+EM2:0:130::6+++++++DA'
UNT+13+1'
UNZ+1+1'

UNA 세그먼트는 선택 사항입니다. 있는 경우 나머지 메시지를 해석하는 데 사용할 특수 문자를 지정합니다. UNA 다음에이 순서대로 6 개의 문자가 있습니다.

  • 구성 요소 데이터 요소 구분 기호 (:이 샘플에서)
  • 데이터 요소 구분 기호 (이 샘플에서 +)
  • 소수 알림 (이 샘플에서는.)
  • 릴리스 문자 (이 샘플에서?)
  • 예약 된 공간이어야합니다.
  • 세그먼트 종결 자 (이 샘플에서 ')

보시다시피 파싱을 기다리는 특별한 방식으로 포맷 된 일부 데이터 일뿐입니다 ( XML 파일 과 매우 유사 ).

이제 내 시스템은 PHP로 작성되었으며 각 세그먼트에 대해 정규 표현식을 사용하여 파서를 만들 수 있었지만 문제가 모든 사람이 표준을 완벽하게 구현하는 것은 아닙니다.

일부 공급 업체는 선택적 세그먼트 및 필드를 완전히 무시하는 경향이 있습니다. 다른 사람들은 다른 사람들보다 더 많은 데이터를 보내도록 선택할 수 있습니다. 그래서 파일과 파일이 올바른지 테스트 할 세그먼트와 필드에 대한 유효성 검사기를 작성해야했습니다.

내가 지금 가지고있는 정규 표현식의 악몽을 상상할 수 있습니다. 또한 각 공급 업체마다 정규 표현식을 많이 수정해야하므로 각 공급 업체마다 파서를 작성하는 경향이 있습니다.


질문 :

1- 파일을 구문 분석하는 데 가장 적합한 방법입니까 (정규 표현식 사용)?

2- 파일을 파싱하기위한 더 나은 솔루션이 있습니까 (아마도 이미 준비된 솔루션이 있습니까)? 누락 된 세그먼트 또는 파일이 손상된 경우 표시 할 수 있습니까?

3- 어쨌든 파서를 작성 해야하는 경우 어떤 디자인 패턴이나 방법을 사용해야합니까?

노트:

나는 yacc와 ANTLR에 관한 어딘가를 읽었지만 그것들이 나의 요구와 일치하는지 아닌지는 모른다!


EDIFACT 문법, 파서 및 라이브러리 (Java) 를 본 후 렉서 / 파서를 사용하는 것이 좋을지 궁금합니다. 그것이 내가 파서 결합기를 먼저 시도 할 것입니다. :)
Guy Coder

답변:


18

필요한 것은 진정한 파서입니다. 정규식은 구문 분석이 아닌 어휘 처리를 처리합니다. 즉, 입력 스트림 내에서 토큰을 식별합니다. 구문 분석은 토큰의 컨텍스트입니다. IE는 어디서, 어떤 순서로 진행합니까?

고전적인 구문 분석 도구는 yacc / bison 입니다. 클래식 렉서는 lex / flex 입니다. php는 C 코드 통합을 허용하므로 flex와 bison을 사용하여 파서를 빌드하고 PHP가 입력 파일 / 스트림에서 호출하도록 한 다음 결과를 얻을 수 있습니다.

그것은됩니다 빠르며 ,과와 작업을 훨씬 쉽게 당신이 도구를 이해하면 . Lex와 Yacc 2nd Ed를 읽는 것이 좋습니다 . 오라일리에서. 예를 들어, github 에 makefile과 함께 flex 및 bison 프로젝트를 설정했습니다 . 필요한 경우 창을 위해 크로스 컴파일 할 수 있습니다.

그것은 이다 복잡한, 그러나 당신이 발견으로, 당신이 할 필요가하는 것은 복잡하다. 제대로 작동하는 파서를 위해 수행해야하는 많은 "재료"가 있으며, 기계적인 비트를 다루는 플렉스 및 바이슨이 있습니다. 그렇지 않으면 어셈블리와 동일한 추상화 계층에서 코드 작성이 불가피한 위치에 있습니다.


1
+1 훌륭한 답변, 특히 샘플 파서가 제공된다는 점을 고려하면 좋습니다.
Caleb

@caleb 덕분에 flex / bison을 많이 사용하지만 괜찮은 (읽기 : 복잡한) 예제는 거의 없습니다. 코멘트가 많지 않기 때문에 이것은 최고의 파서가 아닙니다. 따라서 언제든지 업데이트를 보내십시오.
Spencer Rathbun

자세한 답변과 예를 들어 @SpencerRathbun에게 대단히 감사합니다. 필자가 언급 한 용어 (yacc / bison, lex / flex 등)에 대한 경험은 주로 웹 개발에 관한 것입니다. 인가 "렉스와 Yacc에 2 판은" 내가 모든 것을 이해하고 좋은 파서를 구축하기에 충분한? 아니면 먼저 다루어야 할 다른 주제와 자료가 있습니까?
Songo

@songo이 책은 모든 관련 세부 사항을 다루고 있으며 중간 크기 페이지 ~ 300 페이지로 매우 짧습니다. c 또는 언어 디자인 사용은 다루지 않습니다 . 운 좋게도 K & R The C Programming Language 와 같은 많은 c 참조가 있으며 언어 를 디자인 할 필요가 없으며 참조한 표준을 따르십시오. 저자는 무언가를 한 번 언급하고 필요한 경우 돌아가서 다시 읽을 것이라고 가정하기 때문에 엄폐를 읽는 것이 좋습니다. 그렇게하면 아무것도 놓치지 않습니다.
스펜서 Rathbun

표준 렉서가 UNA 라인이 지정할 수있는 동적 구분 기호를 처리 할 수 ​​있다고 생각하지 않습니다. 따라서 적어도 5 개의 구분 기호에 대해 런타임 사용자 정의 가능 문자가있는 렉서가 필요합니다.
케빈

3

아야 .. '진정한'파서? 상태 머신 ??

미안하지만 취업을 시작한 이래로 학계에서 해커로 전환되었습니다. 그래서 더 쉬운 방법이 있다고 말할 것입니다.

일부는 동의 할 수도 있고 동의하지 않을 수도있는 대체 접근 방식을 제공하려고하지만 작업 환경에서 매우 실용적 일 수 있습니다.

나는 할 것이다;

loop every line
   X = pop the first 3 letters of line
   Y = rest of line
   case X = 'UNA':
       class init (Y)

거기에서 데이터 형식에 클래스를 사용합니다. 구성 요소 및 요소 구분 기호를 분할하고 반환 된 배열을 반복합니다.

나에게 이것은 코드 재사용, OO, 응집력이 낮고 모듈성이 뛰어나며 디버깅 및 프로그래밍이 쉽습니다. 간단할수록 좋습니다.

상태 머신이나 완전히 복잡한 것이 필요없는 파일을 구문 분석하려면 상태 머신이 코드를 구문 분석하는 데 적합하므로 OO 컨텍스트에서 사용될 때 위의 pseduo 코드가 얼마나 강력한 지 놀라게 될 것입니다.

추신. 나는 전에 매우 비슷한 파일로 작업했습니다 :)


더 많은 의사 코드가 여기에 게시되었습니다.

수업

UNA:

init(Y):
 remove ' from end
 components = Y.split(':') 
 for c in components
     .. etc..

 getComponents():
   logic..
   return

 getSomethingElse():
   logic..
   return

class UNZ:
   ...

Parser(lines):

Msg = new obj;

for line in lines
   X = pop the first 3 letters of line
   Y = rest of line
   case X = 'UNA':
      Msg.add(UNA(Y))

msg.isOK = true
return Msg

그런 다음 이렇게 사용할 수 있습니다 ..

msg = Main(File.getLines());
// could put in error checking
// if msg.isOK:
msg.UNA.getSomethingElse();

그리고 둘 이상의 세그먼트가 있다고 가정합니다. 대기열을 사용하여 세그먼트를 추가하고 필요에 따라 첫 번째, 두 번째 등을 가져옵니다. 당신은 실제로 msg를 obj로 표현하고 데이터를 호출하는 객체 메소드를 제공합니다. 당신은 또한 커스텀 메소드를 만들어서 이것을 활용할 수 있습니다.


3
이전에이 작업을 수행했으며, 한 두 가지 경우 이상으로는 충분하지 않습니다 recognize X token and do Y. 상황이 없으며 여러 상태를 가질 수 없으며 사소한 수의 사례를 지나쳐 코드가 부풀어 오르고 오류 처리가 어렵습니다. 거의 모든 경우에 현실 세계에서 이러한 기능이 필요하다는 것을 알았습니다. 복잡성이 커짐에 따라 실수는 제외됩니다. 가장 어려운 부분은 골격을 설정하고 도구 작동 방법을 배우는 것입니다. 그것을지나 치면 무언가를 빨리 채울 수 있습니다.
Spencer Rathbun

그것은 어떤 상태가 필요한 메시지입니까? 복합 및 세그먼트 구조로 구성된 이러한 메시지는이 OO 접근 방식에 완벽하게 맞을 것 같습니다. 오류 처리는 클래스별로 수행되며 올바르게 수행되므로 매우 효율적이고 확장 가능한 파서를 구성 할 수 있습니다. 이와 같은 메시지는 특히 여러 공급 업체가 동일한 형식의 다른 특징을 보낼 때 클래스와 함수에 적합합니다. 특정 공급 업체에 대해 특정 값을 반환 한 UNA 클래스의 함수를 예로들 수 있습니다.
Ross

@Ross 그래서 기본적으로 당신은있을 것이다 "UNA 클래스" 세그먼트에 대한 "UNA" (각 공급 업체에 대한 해석 방법이 될 것이며 그 안에 parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3(), ... 등), 맞죠?
Songo

2
@Ross 파싱하는 동안 다른 지점에서 유효한 메시지 섹션이 있습니다. 그것들은 내가 말한 상태입니다. OO 디자인은 영리하며 작동 하지 않는다고 말하는 것은 아닙니다 . 함수형 프로그래밍 개념처럼 다른 도구보다 적합하기 때문에 flex와 bison을 사용하지만 대부분의 사람들은 학습을 방해하기에는 너무 복잡하다고 생각합니다.
Spencer Rathbun

@ Songo .. 아니, 당신은 공급 업체와 독립적으로 파싱 할 것입니다 (새로운 사람이 아닌 한). 구문 분석은 클래스의 INIT에 있습니다. 메시지를 구성하는 데 사용 된 것과 동일한 규칙에 따라 메시지를 데이터 개체로 바꿉니다. 그러나 메시지에서 무언가를 가져와야하는데 벤더에 따라 다르게 표시되는 경우 다른 기능을 사용할 수 있습니다. 그러나 왜 그렇게됩니까? 기본 클래스를 사용하고 각 공급 업체마다 별도의 클래스를 사용하여 필요한 경우에만 훨씬 쉽게 재정의합니다. 상속을 이용하십시오.
Ross

1

"PHP EDIFACT"에 대한 인터넷 검색을 시도 했습니까? 이것은 http://code.google.com/p/edieasy/ 에 나타난 첫 번째 결과 중 하나입니다 .

사용 사례로는 충분하지 않지만 아이디어를 얻을 수 있습니다. 나는 많은 중첩 for 루프와 조건을 가진 코드를 좋아하지 않지만 시작 일 수 있습니다.


1
나는 많은 프로젝트를 조사했지만 문제는 주로 표준을 사용하는 공급 업체의 다른 구현에있었습니다. 한 공급 업체가 특정 세그먼트를 보내도록 강요 할 수 있지만 다른 공급 업체의 경우 선택적인 것으로 간주 할 수 있습니다. 그렇기 때문에 어쨌든 나만의 맞춤형 파서를 만들어야 할 것입니다.
Songo

1

Yacc / Bison + Flex / Lex가 언급 된 이후 다른 주요 대안 중 하나 인 파서 결합기 (parser combinators)도 사용할 수 있습니다. 이것들은 Haskell과 같은 함수형 프로그래밍에서 인기가 있지만 C 코드에 인터페이스 할 수 있다면 그것들을 사용할 수 있으며 누군가 아는 사람도 PHP 용으로 작성했습니다. (나는 그 특정 구현에 대한 경험이 없지만 대부분의 방식으로 작동한다면 꽤 좋을 것입니다.)

일반적인 개념은 작고 정의하기 쉬운 파서 (일반적으로 토크 나이저)로 시작한다는 것입니다. 언급 한 6 가지 데이터 요소 각각에 대해 하나의 파서 기능이 있습니다. 그런 다음 결합기 (함수를 결합하는 함수)를 사용하여 더 큰 요소를 가져 오는 더 큰 파서를 만듭니다. 선택적인 세그먼트와 마찬가지로 optional세그먼트 파서에서 작동하는 결합기가 있습니다.

PHP에서 얼마나 잘 작동하는지 잘 모르겠지만 파서를 작성하는 재미있는 방법이며 다른 언어로 사용하는 것을 매우 좋아합니다.


0

정규 표현식을 사용하는 대신 자신의 상태 시스템을 만드십시오.

이것은 사소한 상황에서 더 읽기 쉽고 (더 나은 주석을 가질 수 있음) 정규식 인 블랙 박스를 디버깅하기가 더 쉽습니다.


5
참고로, 이것은 플렉스와 바이슨이 후드에서하는 일입니다. 그들만이 제대로한다 .
스펜서 Rathbun

0

나중에이 데이터로 정확히 무엇을하고 싶고 그것이 너트의 망치가 아니라면 eli에 대한 좋은 경험이 있습니다. 어휘 문구를 기술 한 다음 구체적 / 추상적 구문을 설명하고 생성하려는 것을 생성합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.