Jquery-csv eklentisinde kullanılan CSV ayrıştırıcısı
Temel bir Chomsky Tip III gramer ayrıştırıcısı.
Regex tokenizer, verileri karakter bazında değerlendirmek için kullanılır. Bir kontrol karakteriyle karşılaşıldığında, başlangıç durumuna bağlı olarak daha fazla değerlendirme yapmak için kod bir switch deyimine geçirilir. Kontrol dışı karakterler, gerekli dize kopyalama işlemlerinin sayısını azaltmak için toplu olarak gruplandırılır ve kopyalanır.
Belirteç:
var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;
İlk eşleşme kümesi kontrol karakterleridir: değer sınırlayıcı (") değer ayırıcı (,) ve giriş ayırıcı (yeni satırın tüm varyasyonları) Son eşleşme kontrol dışı karakter grubunu işler.
Ayrıştırıcının yerine getirmesi gereken 10 kural vardır:
- Kural # 1 - Her satıra bir giriş, her satır yeni bir satırla bitiyor
- Kural # 2 - İhmal edilen dosyanın sonunda son satır
- Kural # 3 - İlk satır başlık verisi içeriyor
- Kural # 4 - Boşluklar veri olarak kabul edilir ve girdiler sonunda virgül içermemelidir
- Kural 5 - Satırlar çift tırnakla sınırlandırılabilir veya sınırlandırılmayabilir
- Kural # 6 - Satır sonları, çift tırnak işaretleri ve virgüller içeren alanlar çift tırnak işaretleri içine alınmalıdır
- Kural # 7 - Alanları içine almak için çift tırnak işareti kullanılıyorsa, o zaman bir alan içinde görünen çift tırnak işareti, başka bir çift tırnak işareti ile başlayarak önüne geçilmelidir.
- Değişiklik # 1 - Belirtilmeyen bir alan olabilir veya olabilir
- Değişiklik # 2 - Alınan bir alan olabilir veya olmayabilir
- Değişiklik # 3 - Bir girdideki son alan boş değer içerebilir veya içermeyebilir
Not: İlk 7 kural doğrudan IETF RFC 4180'den alınmıştır . Son 3, varsayılan olarak tüm değerleri sınırlamayan (yani alıntı yapan) modern elektronik tablo uygulamaları (örneğin, Excel, Google Elektronik Tablo) tarafından sunulan son durumları kapsayacak şekilde eklendi. Değişiklikleri RFC'ye geri göndermeye çalıştım, ancak soruşturmamı henüz yanıtlamadılar.
Kuruma kadar yeter, işte şema:
Devletler:
- bir giriş ve / veya değer için başlangıç durumu
- bir açılış teklifi ile karşılaşıldı
- ikinci bir teklifle karşılaşıldı
- alıntılanmamış bir değerle karşılaşıldı
Geçişler:
- a. alıntılanan değerleri (1), alıntılanmamış değerleri (3), boş değerleri (0), boş girişleri (0) ve yeni girişleri (0) denetler
- b. ikinci bir alıntı char için kontrol eder (2)
- c. Çıkış teklifini (1), değerin (0) ve girişin (0) sonu kontrol eder
- d. değerin (0) sonunu ve girişin (0) sonunu denetler
Not: Aslında bir devlet eksik. 'C' -> 'b' ile '1' durumuyla işaretlenmiş bir satır olmalıdır, çünkü kaçan ikinci bir sınırlayıcı, ilk sınırlayıcının hala açık olduğu anlamına gelir. Aslında, onu başka bir geçiş olarak göstermek muhtemelen daha iyi olacaktır. Bunları yaratmak bir sanattır, tek bir doğru yol yoktur.
Not: Ayrıca bir çıkış durumu da eksiktir, ancak geçerli verilerde ayrıştırıcı her zaman 'a' geçişiyle sona erer ve ayrıştırılacak hiçbir şey olmadığı için hiçbir durum mümkün değildir.
Durumlar ve Geçişler Arasındaki Fark:
Bir devlet sonludur, yani yalnızca bir şeyi ifade ettiği sonucuna varılabilir.
Bir geçiş, devletler arasındaki akışı temsil eder, böylece birçok anlama gelebilir.
Temel olarak devlet -> geçiş ilişkisi 1 -> * (yani bir-çok). Devlet “ne olduğunu” ve geçiş “nasıl işlendiğini” tanımlar.
Not: Durumların / geçişlerin uygulaması sezgisel hissetmiyorsa endişelenmeyin, sezgisel değildir. Sonunda yapışmadan önce, benden çok daha zeki biriyle mukabil olmuştu.
Sahte Kod:
csv = // csv input string
// init all state & data
state = 0
value = ""
entry = []
output = []
endOfValue() {
entry.push(value)
value = ""
}
endOfEntry() {
endOfValue()
output.push(entry)
entry = []
}
tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm
// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
switch(state) {
case 0:
if(opening delimiter)
state = 1
break
if(new-line)
endOfEntry()
state = 0
break
if(un-delimited data)
value += match
state = 3
break
case 1:
if(second delimiter encountered)
state = 2
break
if(non-control char data)
value += match
state = 1
break
case 2:
if(escaped delimiter)
state = 1
break
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
case 3:
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
}
}
Not: Bu özü, pratikte dikkate alınması gereken daha çok şey var. Örneğin, hata kontrolü, boş değerler, sonunda boş bir satır (geçerli olan) vb.
Bu durumda, durum, regex eşleştirme bloğu bir yinelemeyi tamamladığında durumların durumudur. Geçiş, durum ifadeleri olarak temsil edilir.
İnsanlar olarak, düşük seviyeli işlemleri daha yüksek seviyeli özetlere basitleştirme eğilimindeyiz, ancak bir FSM ile çalışmak düşük seviyeli işlemlerle çalışıyor. Durumlar ve geçişler bireysel olarak çalışmak çok kolay olsa da, hepsini bir kerede görselleştirmek zordur. Geçişlerin nasıl yürüdüğünü sezene kadar, bireysel uygulama yollarını tekrar tekrar izlemeyi en kolay buldum. Temel matematik öğrenmek gibi bir şeydir, düşük seviye detayları otomatikleşmeye başlayana kadar kodu daha yüksek bir seviyeden değerlendirme olanağınız olmayacaktır.
Bir kenara: Asıl uygulamaya bakarsanız, birçok ayrıntı eksik. İlk olarak, tüm imkansız yollar özel istisnalar atar. Onlara vurmak imkansız olmalı, ancak bir şey koparsa test koşucusunda istisnaları kesinlikle tetikleyecektir. İkincisi, 'yasal' bir CSV veri dizisinde izin verilenler için ayrıştırıcı kurallar oldukça gevşek olduğundan kod birçok özel durumla başa çıkmak için gerekli. Bu durumdan bağımsız olarak, bu, tüm hata düzeltmelerinden, uzantılardan ve ince ayarlardan önce FSM ile alay etmek için kullanılan işlemdi.
Çoğu tasarımda olduğu gibi, uygulamanın kesin bir temsili değildir, ancak önemli kısımları ana hatlarıyla belirtir. Uygulamada, aslında bu tasarımdan türetilmiş 3 farklı ayrıştırıcı işlev vardır: csv'ye özgü bir satır ayırıcı, tek satırlı bir ayrıştırıcı ve tam bir çok satırlı ayrıştırıcı. Hepsi benzer şekilde çalışır, yeni satır karakterlerini kullanma şekilleri farklıdır.