Bir dosyayı ayrıştırmanın en iyi yolu


9

EDIFACT ve TRADACOMS gibi bazı ünlü dosya formatlarına ayrıştırıcı yapmak için daha iyi bir çözüm bulmaya çalışıyorum .

Bu standartlara aşina değilseniz Wikipedia'dan bu örneği inceleyin:

Ürün kullanılabilirliği talebini yanıtlamak için kullanılan bir EDIFACT mesajı örneği için aşağıya bakın: -

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 segmenti isteğe bağlıdır. Varsa, iletinin geri kalanını yorumlamak için kullanılacak özel karakterleri belirtir. Bu sırayla UNA'yı takip eden altı karakter var:

  • bileşen veri elemanı ayırıcı (: bu örnekte)
  • veri elemanı ayırıcısı (bu örnekte +)
  • ondalık bildirim (. bu örnekte)
  • serbest bırakma karakteri (? bu örnekte)
  • ayrılmış, boşluk olmalı
  • segment sonlandırıcısı (bu örnekte ')

Gördüğünüz gibi, sadece ayrıştırılmayı bekleyen özel bir şekilde biçimlendirilmiş bazı veriler ( XML dosyaları gibi ).

Şimdi sistemim PHP üzerine kurulu ve ben her segment için düzenli ifadeler kullanarak ayrıştırıcı oluşturmak başardı, ama sorun herkes standart mükemmel uygular değildir.

Bazı tedarikçiler, isteğe bağlı segmentleri ve alanları tamamen göz ardı etme eğilimindedir. Diğerleri diğerlerinden daha fazla veri göndermeyi seçebilir. Bu nedenle, dosyanın doğru olup olmadığını test etmek için segmentler ve alanlar için doğrulayıcılar oluşturmak zorunda kaldım.

Şu anda düzenli ifadelerin kabusunu hayal edebilirsiniz. Buna ek olarak, her tedarikçi, her tedarikçi için bir ayrıştırıcı oluşturma eğiliminde olduğum düzenli ifadelerde birçok değişikliğe ihtiyaç duyar.


Sorular:

1- Bu dosyaları ayrıştırmak için en iyi yöntem midir (düzenli ifadeler kullanarak)?

2- Dosyaları ayrıştırmak için daha iyi bir çözüm var mı (belki orada hazır çözüm var)? Hangi segmentin eksik olduğunu veya dosyanın bozuk olup olmadığını gösterebilecek mi?

3- Ayrıştırıcımı yine de inşa etmem gerekirse hangi tasarım desenini veya metodolojisini kullanmalıyım?

Notlar:

Ycc ve ANTLR hakkında bir yer okudum ama ihtiyaçlarımı karşılayıp karşılamadıklarını bilmiyorum!


Bu EDIFACT dilbilgisi, ayrıştırıcılar ve kütüphaneler (Java) gördükten sonra bir lexer / ayrıştırıcı kullanmanın işe yaradığını merak ediyorum. Eğer ben olsaydım önce ayrıştırıcı birleştiriciyi denerdim. :)
Guy Coder

Yanıtlar:


18

İhtiyacınız olan şey gerçek bir ayrıştırıcıdır. Düzenli ifadeler ayrıştırmayı değil lexing'i işler. Yani, giriş akışınızdaki belirteçleri tanımlarlar. Ayrıştırma, jetonların bağlamıdır, IE nereye ve hangi sırayla gider.

Klasik ayrıştırma aracı yacc / bison'dur . Klasik lexer lex / flex'dir . Php C kodunu entegre etmesine izin verdiğinden , ayrıştırıcınızı oluşturmak için flex ve bizon kullanabilirsiniz, giriş dosyasında / akışında php çağrısı yapabilir ve sonuçlarınızı alabilirsiniz.

Bu edilecek hızlı yanan ve birlikte çalışmak için çok daha kolay aletleri anladıktan sonra . Lex ve Yacc 2nd Ed'i okumanızı tavsiye ederim . O'Reilly'den. Örneğin , github üzerinde bir makefile ile esnek ve bizon projesi kurdum . Gerekirse pencereler için çapraz derlenebilir.

Bu ise karmaşık ama öğrendim olarak, ne yapılması gerek karmaşıktır. Düzgün çalışan bir ayrıştırıcı için yapılması gereken çok sayıda "malzeme" ve mekanik bitlerle esnek ve bizon anlaşması vardır. Aksi takdirde, kendinizi kodla aynı soyutlama katmanında yazma kodunun kaçınılmaz konumunda bulursunuz.


1
+1 Özellikle bir örnek ayrıştırıcıyla birlikte geldiği düşünülürse harika yanıt.
Caleb

@ caleb teşekkürler, flex / bison ile çok çalışıyorum, ancak çok az iyi (okuma: karmaşık) örnek var. Bu, şimdiye kadarki en iyi ayrıştırıcı değil, çünkü çok fazla yorum yok, bu yüzden güncellemeleri göndermek için çekinmeyin.
Spencer Rathbun

@SpencerRathbun detaylı cevap ve örnek için çok teşekkürler. Bahsettiğim terminoloji hakkında ne kadar bilgim yok (yacc / bison, lex / flex, ... vb.) Çünkü deneyimim esas olarak web geliştirme ile ilgili. Mı "Lex ve Yacc 2.baskı" bana her şeyi anlamak ve iyi bir ayrıştırıcı inşa etmek için yeterli? veya önce ele almam gereken başka konular ve materyaller var mı?
Songo

@songo Kitap tüm ilgili detayları kapsamaktadır ve ~ 300 orta boyutlu sayfalara göre oldukça kısadır. C veya dil tasarımını kapsamaz . Neyse ki, K&R The C Programming Language gibi birçok c referansı vardır ve bir dil tasarlamanız gerekmez, sadece referans verdiğiniz standartları takip edin. Yazarların bir kez bahsettikleri ve ihtiyacınız varsa geri dönüp tekrar okuyacağınızı varsayacağından, kitap kapağı okumak için tavsiye edildiğini lütfen unutmayın. Bu şekilde hiçbir şeyi kaçırmazsınız.
Spencer Rathbun

Standart bir lexer'ın UNA satırının belirleyebileceği dinamik ayırıcıları işleyebileceğini sanmıyorum. En azından 5 ayırıcı için çalışma zamanı özelleştirilebilir karakterlere sahip bir lexer'a ihtiyacınız olacak.
Kevin

3

ah .. 'gerçek' ayrıştırıcı? devlet makineleri ??

üzgünüm ama ben işe başladığımdan beri akademik hacker dönüştürüldü .. bu yüzden daha kolay yollar olduğunu söyleyebilirim .. belki de akademik olarak 'rafine' gibi olmasa da :)

Bazılarının hemfikir olabileceği veya olmayabileceği alternatif bir yaklaşım sunmaya çalışacağım, ancak çalışma ortamında çok pratik OLABİLİR.

İsterim;

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

oradan veri türleri için sınıfları kullanırdım. bileşen ve eleman ayırıcılarını ayırma ve döndürülen diziler üzerinde yineleme.

Benim için, bu kod yeniden kullanım, OO, düşük uyum ve yüksek modüler .. ve hata ayıklama ve programlanması kolaydır. daha basit daha iyidir.

devlet makineleri veya tamamen karmaşık bir şey gerekmez bir dosyayı ayrıştırmak için .. devlet makineleri kodu ayrıştırmak için çok uygundur, bir OO bağlamında kullanıldığında yukarıdaki pseduo kodunun ne kadar güçlü olabileceğinden şaşıracaksınız.

ps. daha önce çok benzer dosyalarla çalıştım :)


Burada daha fazla sözde kod gönderildi:

sınıf

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

daha sonra bu şekilde kullanabilirsiniz ..

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

ve birden fazla segmentiniz olduğunu söyleyin .. onları eklemek için bir kuyruk kullanın ve ihtiyacınız olan ilk, ikinci vb .. olsun. Gerçekten sadece bir obj için msg temsil ve veri aramak için nesne yöntemleri veriyoruz. Ayrıca özel yöntemler oluşturarak bundan yararlanabilirsiniz .. miras için .. bu farklı bir soru ve eğer anlarsanız kolayca uygulayabileceğinizi düşünüyorum


3
Bunu daha önce yaptım ve bir veya iki vakanın ötesinde bir şey için yetersiz olduğunu gördüm recognize X token and do Y. Bağlam yoktur, birden fazla durumunuz olamaz, önemsiz sayıda kodu geçerek kodu bloke eder ve hata işlemesi zordur. Hemen hemen her durumda gerçek dünyada bu özelliklere ihtiyacım olduğunu düşünüyorum. Bu, karmaşıklık arttıkça hataları bir kenara bırakır. En zor kısmı bir iskelet kurmak ve aracın nasıl çalıştığını öğrenmek. Bunu aş ve bir şeyi kırbaçlamak kadar hızlı.
Spencer Rathbun

bu bir mesaj, hangi eyaletlere ihtiyacınız var? kompozitlerin ve segmentlerin yapısında düzenlenen böyle bir mesajın bu OO yaklaşımına mükemmel bir şekilde uyduğu görülüyor. hata işleme sınıf başına yapılır ve düzgün bir şekilde yapılırsa çok verimli ve genişletilebilir bir ayrıştırıcı oluşturabilirsiniz. bunun gibi mesajlar, özellikle birden fazla tedarikçi aynı formatta farklı tatlar gönderdiğinde kendilerini sınıflara ve işlevlere verir. Bir örnek, UNA sınıfında belirli bir satıcı için belirli bir değer döndüren bir işlev olabilir.
Ross

@Ross böylece temelde bir olacak "UNA sınıf" segment için "UNA" (her satıcı için bir ayrıştırma yöntemi olacak ve içindeki parseUNAsegemntForVendor1(), parseUNAsegemntForVendor2(), parseUNAsegemntForVendor3(), ... vb), doğru mu?
Songo

2
@Ross Mesajda, ayrıştırma sırasında farklı noktalarda geçerli olan bölümler var. Bunlar bahsettiğim devletler. OO tasarımı akıllı ve işe yaramayacağını söylemiyorum . Esnek ve bizonları itiyorum çünkü fonksiyonel programlama kavramları gibi, diğer araçlardan daha iyi uyuyorlar, ancak çoğu insan öğrenmeyi rahatsız etmek için çok karmaşık olduklarına inanıyor.
Spencer Rathbun

@Songo .. hayır, satıcıdan bağımsız olarak ayrıştırırsınız (yeni kim olmadıkça). ayrıştırma sınıfın INIT'inde olacaktır. İletinizi, iletiyi oluşturmak için kullanılan kurallara göre bir veri nesnesine dönüştürürsünüz. Ancak mesajdan bir şey almanız gerekiyorsa .. ve satıcılarınız arasında farklı bir şekilde temsil edilirse, o zaman farklı fonksiyonlara sahip olursunuz .. Ama neden böyle? bir temel sınıf kullanın ve her satıcı için ayrı bir sınıfa sahip olun, yalnızca gerektiğinde geçersiz kılın, çok daha kolay. kalıtımdan yararlanmak.
Ross

1

"PHP EDIFACT" için googling yapmayı denediniz mi? Bu, ortaya çıkan ilk sonuçlardan biridir: http://code.google.com/p/edieasy/

Kullanım durumunuz için yeterli olmasa da, bundan bazı fikirler alabilirsiniz. Döngüler ve koşullar için iç içe birçok kodunu sevmiyorum, ama bir başlangıç ​​olabilir.


1
Orada birçok projeyi kontrol ettim, ancak sorun esas olarak standardı kullanan satıcıların farklı uygulamalarındaydı. Bir satıcıyı bana belirli bir segmenti göndermeye zorlayabilirim, ancak başka bir satıcı için isteğe bağlı olduğunu düşünebilirim. Bu yüzden muhtemelen kendi özelleştirilmiş ayrıştırıcımı oluşturmam gerekecek.
Songo

1

Yacc / Bison + Flex / Lex'den bahsedildiği için, diğer önemli alternatiflerden birini de atabilirim: ayrıştırıcı birleştiriciler. Bunlar Haskell gibi fonksiyonel programlamada popülerdir, ancak C koduna arayüz oluşturabiliyorsanız bunları kullanabilirsiniz ve ne biliyorsunuz, biri PHP için de bir tane yazdı. (Bu uygulama ile ilgili hiçbir deneyimim yok, ancak çoğu gibi çalışıyorsa, oldukça güzel olmalı.)

Genel konsept, bir dizi küçük, tanımlaması kolay ayrıştırıcı, genellikle belirteçlerle başlamanızdır. Bahsettiğiniz 6 veri öğesinin her biri için bir ayrıştırıcı işleviniz varmış gibi. Daha sonra, daha büyük elemanları tutan daha büyük ayrıştırıcılar yapmak için birleştiricileri (işlevleri birleştiren işlevler) kullanırsınız. İsteğe bağlı bir segment gibi optional, segment ayrıştırıcıda çalışan birleştirici de olabilir .

PHP'de ne kadar iyi çalıştığından emin değilim, ancak ayrıştırıcı yazmanın eğlenceli bir yolu ve onları diğer dillerde kullanmaktan çok keyif alıyorum.


0

regexes ile uğraşmak yerine kendi devlet makinenizi yapın

bu önemsiz durumlarda daha okunabilir (ve daha iyi yorumlara sahip olabilir) ve regex olan kara kutuda hata ayıklamak daha kolay olacaktır


5
Kısa bir not, bu esnekliğin ve bizonun kaputun altında yaptığı şeydir. Sadece onlar bunu doğru .
Spencer Rathbun

0

Daha sonra bu verilerle tam olarak ne yapmak istediğinizi bilmiyorum ve bir fındık için balyoz değilse, ama eli ile iyi deneyimlerim vardı . Sözcüksel ifadeleri ve daha sonra somut / soyut sözdizimini tanımlar ve oluşturmak istediğiniz şeyi yaratırsınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.