Kısaca
Sorununuza en hızlı çözüm, belgelerin olası tüm başlangıçlarını tanıyan bir REGEX veya bir FSA (sonlu durum otomatiği) tanımlamak gibi görünüyor (aslında bir belgeye karşılık gelmeyen yanlış pozitiflere izin verilir). Ardından, bir belgenin birkaç hatayla başlayabileceği bir sonraki yeri tanımlamak için girdilerinizde çok hızlı şekilde çalıştırabilirsiniz. Bir belge başlangıcı için birkaç hatalı konuma neden olabilir, ancak ayrıştırıcı tarafından tanınacak ve terk edilecektir.
Bu yüzden Sonlu Durum Otomatı , aradığınız çözümleyici adı olabilir. :)
Sorun
Pratik bir sorunu anlamak her zaman zordur, özellikle de kelime haznesinde çok fazla yorum olduğunda. Ayrıştırma ormanı kelimesi, çeşitli ayrıştırma ağaçlarına sahip belirsiz cümlelerin ayrıştırılması için Bağlamsız (CF) kelimesiyle ayrıştırıldı (afaik). Bir cümle örgüsünü veya diğer dilbilgisi türlerini ayrıştırmak için genelleştirilebilir. Dolayısıyla, Earley, GLR, Marpa ve türev ayrıştırıcılarla ilgili tüm cevaplar (bu konuda pek de geçerli değil) var.
Ama görünüşe göre aklınızdaki şey bu değil. Belli başlı belgelerin bir dizisi olan benzersiz bir dize ayrıştırmak ve her biri için bir ayrıştırma ağacı veya bir tür yapılandırılmış gösterim elde etmek istiyorsunuz; resmi bir dil bakış açısı. Elinizde olan, bir belgenin başında başlatıldığında ayrıştırma işini yapacak bir algoritma ve tablolardır. Öyle olsun.
Asıl sorun, doküman akışınızın dokümanları ayıran önemli miktarda çöp içermesidir. Görünen o ki, senin zorluğun bu çöpleri yeterince hızlı taramak. Şu anki tekniğiniz en baştan başlamak ve ilk karakterden taramaya çalışmak ve tüm belgeyi taranana kadar bir sonraki karakterde yeniden başlatmaya geçmek. Ardından, belge tarandıktan sonra ilk karakterden gelen ifadeleri tekrarlayın.
Bu aynı zamanda cevabın ikinci bölümünde @ amon tarafından önerilen çözümdür .
Çok hızlı bir çözüm olmayabilir (test edemem), çünkü çözümleyici kodunun bir belgenin başlangıcında çok verimli bir şekilde başlatılması için optimize edilmiş olması olası değildir. Normal kullanımda bunu yalnızca bir kez yapar, böylece optimizasyon açısından sıcak bir nokta değildir. Bu nedenle, bu çözüm ile ılımlı mutluluğunuz çok şaşırtıcı değil.
Öyleyse gerçekten ihtiyacınız olan, hızlı bir şekilde bir çöp kütlesiyle başlayan bir belgenin başlangıcını bulabilen bir algoritma. Ve şanslısınız: bu tür algoritmalar var. Ve bildiğinizden eminim: buna bir REGEX aramak denir.
Basit bir çözüm
Yapmanız gereken, bu belgelerin nasıl başladığını bulmak için belgelerinizin özelliklerini analiz etmektir. Size tam olarak nasıl söyleyemem, onların sözdizimi özelliklerinin biçimsel olarak nasıl düzenlendiğinden emin değilim. Muhtemelen hepsi sınırlı bir listeden bir sözcükle başlar, muhtemelen bazı noktalama işaretleri veya sayılarla karıştırılırlar. Bu senin kontrol etmen için.
Yapmanız gereken, sonlu durumlu bir otomatiği (FSA) tanımlamak veya çoğu programcı için eşdeğerde bir belgenin ilk birkaç karakterini tanıyabilen normal bir ifade (REGEX) tanımlamaktır: ne kadar çok, o kadar iyi, ama çok fazla olması gerekmez büyük (bu zaman ve mekan alabilir). Bu, belgelerinizin özelliklerini belirlemek için nispeten kolay olmalı ve muhtemelen belgelerinizin özelliklerini okuyan bir programla otomatik olarak yapılabilir.
Regexp'inizi oluşturduktan sonra, ilk (veya sonraki) belgenizin başlangıcına çok hızlı bir şekilde ulaşmak için giriş akışınızda çalıştırabilirsiniz:
Ben varsayalım:
- docstart
tüm belgelerin başlangıcını eşleşen bir düzenli ifade ise
- search(regex, stream)
bir işlev olduğunu aramalar stream
eşleştiği bir alt dizeyi regex
. Döndüğünde, akış ilk eşleşen alt dizenin başlangıcından başlayarak sonek alt akışına indirgenir veya boş akışa eşleşme bulunmaz.
- parse(stream)
bir belgeyi akışın başından itibaren (geriye kalanı) ayrıştırmaya çalışır ve ayrıştırma ağacını ne olursa olsun veya geri döndürür. Geri döndüğünde akış, ayrıştırılan belgenin sonunu hemen takip eden pozisyondan başlayarak sonek alt akışına indirgenir. Ayrıştırma başarısız olursa bir istisna çağırır.
forest = empty_forest
search(docstart, stream)
while stream is not empty:
try:
forest = forest + parse(stream)
except
remove first character from stream
search(docstart, stream)
İlk karakterin kaldırılması gerektiğine dikkat edin, böylece sonraki arama aynı eşleşmeyi tekrar bulamaz.
Elbette akışın kısaltılması bir görüntüdür. Sadece akışta bir dizin olabilir.
Son bir not, regex'iniz tüm başlangıçları tanıdığı sürece çok doğru olması gerekmediğidir. Zaman zaman bir belgenin başlangıcı olamayacak bir dize tanırsa (yanlış pozitif), o zaman tek ceza ayrıştırıcıya gereksiz bir çağrının maliyetidir.
Böylece, eğer mümkünse regex'in basitleştirilmesine yardımcı olabilir.
Daha hızlı bir çözüm olasılığı hakkında
Yukarıdaki çözüm çoğu durumda oldukça iyi çalışmalıdır. Bununla birlikte, işlenecek çok fazla çöp ve terabayt dosyanız varsa, daha hızlı çalışan başka algoritmalar olabilir.
Bu fikir Boyer-Moore string arama algoritmasından türetilmiştir . Bu algoritma, tek bir dizge için çok hızlı bir akış arayabilir çünkü dizgenin yapısal bir analizini kullanır, akışın çoğunu okumak, parçalara bile bakmadan atlamak için yapısal bir analiz kullanır. Tek bir dize için en hızlı arama algoritmasıdır.
Zorluk, tek bir dize yerine regex'i aramaya adapte etmenin, düşündüğünüz regex'in özelliklerine bağlı olarak çok narin göründüğü ve işe yaramayabileceğidir. Bu daha sonra ayrıştırdığınız belgelerin sözdizimine bağlı olabilir. Ancak bulduğum belgelerin dikkatlice okunması için zamanım olmadığından bana bu konuda fazla güvenme.
Sizi web’de bulduğum bir ya da iki işaretle bırakıyorum , görünüşe göre hakemli bir araştırma makalesi de dahil olmak üzere , ancak bunu yalnızca güçlü performans problemleriniz varsa düşünülmeli, daha spekülatif, muhtemelen araştırma olarak düşünmelisiniz. Ve muhtemelen bunu yapacak raf programlarından da yok.