ファイルを解析する最良の方法


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文字があります。

  • コンポーネントデータ要素セパレータ(:このサンプルでは)
  • データ要素セパレータ(このサンプルでは+)
  • 10進数の通知(このサンプルでは。)
  • リリースキャラクター(このサンプルでは?)
  • 予約済み、スペースでなければならない
  • セグメントターミネーター(このサンプルでは ')

ご覧のとおり、解析されるのを待機している特別な方法でフォーマットされたデータの一部です(XMLファイルのように)。

現在、私のシステムはPHPで構築されており、セグメントごとに正規表現を使用してパーサーを作成できましたが、問題は誰もが標準を完全に実装しているわけではないということです。

一部のサプライヤーは、オプションのセグメントとフィールドを完全に無視する傾向があります。他の人は他より多くのデータを送信することを選択するかもしれません。そのため、ファイルが正しいかどうかをテストするために、セグメントとフィールドのバリデーターを作成する必要がありました。

私が今持っている正規表現の悪夢を想像できます。さらに、各サプライヤーは、各サプライヤーのパーサーを作成する傾向がある正規表現に多くの変更を加える必要があります。


質問:

1-これは(正規表現を使用して)ファイルを解析するためのベストプラクティスですか?

2-ファイルを解析するためのより良いソリューションはありますか(おそらくそこに既製のソリューションがあるでしょう)?どのセグメントが欠落しているか、またはファイルが破損しているかを表示できますか?

3-とにかくパーサーを構築する必要がある場合、どの設計パターンまたは方法論を使用する必要がありますか?

ノート:

yaccとANTLRについてどこかで読みましたが、私のニーズに合っているかどうかはわかりません。


このEDIFACT文法、パーサー、およびライブラリ(Java)を確認した後、レクサー/パーサーを使用するとうまくいくかどうか疑問に思います。もしそれが私だったら、パーサーのコンビネーターを最初に試します。:)
Guy Coder

回答:


18

必要なのは真のパーサーです。正規表現は構文解析ではなく字句解析を処理します。つまり、入力ストリーム内のトークンを識別します。解析はトークンのコンテキストであり、IEはどこに、どのような順序で進むかを示します。

古典的な解析ツールはyacc / bisonです。古典的なレクサーはlex / flexです。phpではCコード統合できるため、フレックスとバイソンを使用してパーサーを構築し、phpに入力ファイル/ストリームで呼び出して結果を取得できます。

それはされます速く燃える、とはるかに簡単で、作業に使用すると、ツールを理解すればLexとYacc 2nd Edを読むことをお勧めします。オライリーから。例として、makehubを使用して、githubにflex and bisonプロジェクトをセットアップしました。必要に応じて、ウィンドウ用にクロスコンパイルできます。

それ複雑ですが、あなたが知っているように、あなたがする必要があることは複雑です。適切に機能するパーサーのために実行する必要のある「もの」はたくさんあり、フレックスとバイソンは機械的なビットを処理します。それ以外の場合は、アセンブリと同じ抽象化レイヤーでコードを書くといううらやましい立場にいることになります。


1
+1すばらしい回答、特にサンプルパーサーが付属していることを考えると。
カレブ

@calebおかげで、私はflex / bisonを頻繁に使用していますが、適切な(read:complex)例はほとんどありません。コメント数が少ないため、これはこれまでで最高のパーサーではありません。更新を送信してください。
スペンサーラスバン

@SpencerRathbunは、詳細な回答と例を非常に感謝しています。私の経験は主にWeb開発に関するものであるため、あなたが言及した用語(yacc / bison、lex / flexなど)についてこれまで何が起こったのかはわかりません。で「LexとYaccの第2版は、」私はすべてを理解して良いのパーサを構築するための十分な?それとも最初にカバーすべき他のトピックや資料はありますか?
ソンゴ

@songoこの本は関連するすべての詳細をカバーしており、非常に短く、約300の中サイズのページを表示しています。cまたは言語設計の使用については説明していません。幸い、K&R The Cプログラミング言語など、利用可能なcリファレンスはたくさんあり、言語を設計する必要はありません。参照した標準に従ってください。執筆者は一度何かについて言及し、必要に応じて戻って読み直すことを想定しているため、カバートゥカバーを読むことをお勧めします。そうすれば、見逃すことはありません。
スペンサーラスバン

UNA行で指定されている可能性のある動的セパレーターを標準のレクサーで処理できないと思います。したがって、少なくとも、5つの区切り文字用にランタイムでカスタマイズ可能な文字を含むレクサーが必要です。
ケビン

3

ouch .. 'true'パーサー?ステートマシン?

申し訳ありませんが、私は就職以来、アカデミックからハッカーに転向してきました。なので、もっと簡単な方法があると思います。

私は一部の人が同意するかもしれないし、同意しないかもしれない別のアプローチを提供しようと試みますが、それは作業環境では非常に実用的です。

私は...するだろう;

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

そこから、データ型のクラスを使用します。コンポーネントと要素のセパレーターを分割し、返された配列を反復処理します。

私にとって、これはコードの再利用、オブジェクト指向、低凝集性、高度にモジュール化されたものであり、デバッグとプログラミングが簡単です。シンプルな方が良いです。

ファイルを解析するには、ステートマシンやまったく複雑なものは必要ありません。ステートマシンはコードの解析に非常に適しているため、OOコンテキストで使用した場合、上記のpseduoコードがどれほど強力であるかに驚かれるでしょう。

ps。以前に非常によく似たファイルで作業したことがあります:)


ここに投稿されたその他の疑似コード:

クラス

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();

複数のセグメントがあると言います。必要に応じて、キューを使用してそれらを追加し、最初、2番目などを取得します。あなたは本当にmsgをobjに表現し、データを呼び出すためのオブジェクトメソッドを与えているだけです。カスタムメソッドを作成することでこれを利用することもできます..継承について..それは別の質問です。理解すれば簡単に適用できると思います


3
私は以前にそれを行ったことがあり、1〜2つのケースを超えるものには不十分であることがわかりましたrecognize X token and do Y。コンテキストはありません。複数の状態を持つことはできず、ごくわずかなケースを過ぎるとコードが膨らみ、エラー処理が困難になります。私は、ほとんどすべてのケースで、現実の世界でこれらの機能が必要であることがわかりました。複雑さが増すにつれ、そこには間違いが残ります。最も難しいのは、スケルトンをセットアップし、ツールの動作を学ぶことです。それを乗り越えれば、何かを打ち上げるのと同じくらい速いです。
Spencer Rathbun、2012年

それはメッセージです、あなたはどのような状態が必要ですか?コンポジットとセグメントの構造に編成されたこのようなメッセージは、このOOアプローチに完全に適合すると思われます。エラー処理はクラスごとに行われ、適切に行われるため、非常に効率的で拡張可能なパーサーを構築できます。このようなメッセージは、特に複数のベンダーが同じ形式の異なるフレーバーを送信する場合に、クラスや関数に役立ちます。例としては、特定のベンダーの特定の値を返すUNAクラスの関数があります。
ロス

@Rossので、基本的にあなたが持っているだろう「UNAクラス」セグメントの「UNA」(各ベンダーのための解析方法があるでしょうし、その中にparseUNAsegemntForVendor1()parseUNAsegemntForVendor2()parseUNAsegemntForVendor3()、...等)、右?
ソンゴ

2
@Rossメッセージのセクションがあり、解析中のさまざまな時点で有効です。それらは私が話していた州です。OOのデザインは賢いので、機能しないとは言いません。関数型プログラミングの概念と同様に、フレックスとバイソンをプッシュします。これらは他のツールよりもうまく適合しますが、ほとんどの人はそれらが複雑すぎて学習に迷惑をかけると信じています。
スペンサーラスバン

@Songo ..いいえ、ベンダーとは関係なく解析します(新しいユーザーがいない限り)。解析はクラスのINITにあります。メッセージの作成に使用したのと同じルールに基づいて、メッセージをデータオブジェクトに変換します。ただし、メッセージから何かを取得する必要があり、それがベンダー間で異なって表されている場合は、さまざまな機能があります。しかし、なぜそのようになっているのですか?基本クラスを使用し、ベンダーごとに個別のクラスを用意して、必要な場合にのみオーバーライドするほうがはるかに簡単です。継承を利用します。
ロス

1

「PHP EDIFACT」のグーグルを試しましたか?これは最初にポップアップした結果の1つです:http : //code.google.com/p/edieasy/

ユースケースとしては十分ではないかもしれませんが、そこからいくつかのアイデアを得ることができるかもしれません。多くのforループと条件が入れ子になっているコードは好きではありませんが、最初のコードになるかもしれません。


1
私はそこにある多くのプロジェクトをチェックしましたが、問題は主に、標準を使用するベンダーのさまざまな実装にありました。あるベンダーに特定のセグメントを強制的に送信する場合がありますが、別のベンダーの場合はオプションと見なす場合があります。だからとにかく、自分でカスタマイズしたパーサーを作成する必要があるでしょう。
ソンゴ2012年

1

Yacc / Bison + Flex / Lexについて触れたので、他の主要な代替手段の1つであるパー​​サーコンビネーターを投入することもできます。これらはHaskellのような関数型プログラミングで人気がありますが、Cコードにインターフェースできれば、それらを使用できます。また、誰かがPHP用に作成したものも知っています (私はその特定の実装についての経験はありませんが、それがそれらのほとんどのように動作する場合、それはかなり良いはずです。)

一般的な概念は、小さい、簡単に定義できるパーサー、通常はトークナイザーのセットから始めることです。あなたが言及した6つのデータ要素のそれぞれに1つのパーサー関数があるように。次に、コンビネーター(関数を組み合わせる関数)を使用して、より大きな要素を取得するより大きなパーサーを作成します。オプションのセグメントと同様optionalに、セグメントパーサーで動作するコンビネーターになります。

PHPでどれだけうまく機能するかはわかりませんが、パーサーを作成する楽しい方法であり、他の言語での使用をとても楽しんでいます。


0

正規表現をいじるのではなく、独自の状態マシンを作成する

これは重要な状況でより読みやすく(そしてより良いコメントを付けることができるように)なり、正規表現であるブラックボックスであることをデバッグしやすくなります


5
手短に言えば、これはフレックスとバイソンがフードの下で行うことです。彼らだけがそれを正しく行う
Spencer Rathbun

0

このデータを後でどのように正確に処理したいのかわからないし、それがナットのハンマーではない場合でも、eliで良い経験をしました。語彙句を記述してから、具体的/抽象的な構文を記述し、生成したいものを生成します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.