2012-11-19 13 views
5

Bir CSV hattını Rebol bloğuna dönüştürmek için PARSE kullanmaya çalışıyorum. Açık kodda yazmak için yeterince kolay, ama diğer sorularda olduğu gibi, diyalektin ne yapabildiğini öğrenmeye çalışıyorum.Bir CSV'den satır okumak için PARSE lehçesi nasıl kullanılır?

Yani bir çizgi diyorsa: fark

[{Look, that's "MR. Fork" to you!} {Hostile Fork} none {http://hostilefork.com}] 

Sorunlar:

"Look, that's ""MR. Fork"" to you!",Hostile Fork,,http://hostilefork.com 

Sonra bloğu istiyorum

CSV dizeleri
  • Gömülü tırnak ""
  • ile gösterilir
  • Virgül tırnak içinde olabilir ve h literal değil, bir sütun ayırıcı ence parçası
  • Bitişik sütun ayıran virgül tırnak içermez veya virgül Bir şeyleri tutabilir an için
  • tırnaklar olmadan görünebilir yani boş sahadan
  • Dizeleri belirtmek STRING olarak http://rebol.com gibi! yerine LOAD böyle URL!

daha düzgün hale getirmek için sıra tipe bunları ing, ilk işim giriş hattına virgül eklemek olduğunu. Sonra bir virgül ile sonlandırılan tek bir sütun yakalayan bir column-rule var, bu da ya tırnak içinde olabilir ya da olmayabilir.

Ben başlık hattına bağlı olması gerektiğini kaç sütun biliyoruz, bu yüzden kod sonra diyor ki:

unless parse line compose [(column-count) column-rule] [ 
    print rejoin [{Expected } column-count { columns.}] 
] 

Ama biraz column-rule yazma takılıp değilim. ifade etmek için lehçede bir yol ihtiyacım var "Bir alıntı bulduktan sonra, kendi başına ayakta bir teklif buluncaya kadar alıntı çiftleri atlama tutun." Bunu yapmanın iyi bir yolu nedir?

cevap

3

Çoğu ayrıştırma sorununda olduğu gibi, giriş biçiminin öğelerini en iyi tanımlayan bir dilbilgisi oluşturmaya çalışıyorum.

[comma ending value-chars qmark quoted-chars value header row] 

Bazı fiiller:

[row-feed emit-value] 

Ve operatif isimler: Ben muhtemelen bir o yıkmak herhalde

[current chunk current-row width] 

Bu durumda, biz isimler var biraz daha fazla, ama çalışmak için yeterli. İlk olarak, temel değer yapısı:

. Onları bulana kadar Alıntı değerlerin geçerli karakterlerinizle veya tırnak parçaları yukarı inşa edilir:

delimiter her satır başında ending olarak ayarlanır Not olduğunu
current: chunk: none 
quoted-value: [ 
    qmark (current: copy "") 
    any [ 
     copy chunk some quoted-chars (append current chunk) 
     | 
     qmark qmark (append current qmark) 
    ] 
    qmark 
] 

value: [ 
    copy current some value-chars 
    | quoted-value 
] 

emit-value: [ 
    (
     delimiter: comma 
     append current-row current 
    ) 
] 

emit-none: [ 
    (
     delimiter: comma 
     append current-row none 
    ) 
] 

sonra en kısa sürede bir değer geçerken comma değiştirildi.Böylece, bir girdi satırı [ending value any [comma value]] olarak tanımlanmıştır. kalır

Tüm belge yapısını tanımlamaktır:

current-row: none 
row-feed: [ 
    (
     delimiter: ending 
     append/only out current-row: copy [] 
    ) 
] 

width: none 
header: [ 
    (out: copy []) 
    row-feed any [ 
     value comma 
     emit-value 
    ] 
    value body: ending :body 
    emit-value 
    (width: length? current-row) 
] 

row: [ 
    row-feed width [ 
     delimiter [ 
      value emit-value 
      | emit-none 
     ] 
    ] 
] 

if parse/all stream [header some row opt ending][out] 

tüm bu kelimeler kalkan o kadar sarın ve sahip:

REBOL [ 
    Title: "CSV Parser" 
    Date: 19-Nov-2012 
    Author: "Christopher Ross-Gill" 
] 

parse-csv: use [ 
    comma ending delimiter value-chars qmark quoted-chars 
    value quoted-value header row 
    row-feed emit-value emit-none 
    out current current-row width 
][ 
    comma: "," 
    ending: "^/" 
    qmark: {"} 
    value-chars: complement charset reduce [qmark comma ending] 
    quoted-chars: complement charset reduce [qmark] 

    current: none 
    quoted-value: use [chunk][ 
     [ 
      qmark (current: copy "") 
      any [ 
       copy chunk some quoted-chars (append current chunk) 
       | 
       qmark qmark (append current qmark) 
      ] 
      qmark 
     ] 
    ] 

    value: [ 
     copy current some value-chars 
     | quoted-value 
    ] 

    current-row: none 
    row-feed: [ 
     (
      delimiter: ending 
      append/only out current-row: copy [] 
     ) 
    ] 
    emit-value: [ 
     (
      delimiter: comma 
      append current-row current 
     ) 
    ] 
    emit-none: [ 
     (
      delimiter: comma 
      append current-row none 
     ) 
    ] 

    width: none 
    header: [ 
     (out: copy []) 
     row-feed any [ 
      value comma 
      emit-value 
     ] 
     value body: ending :body 
     emit-value 
     (width: length? current-row) 
    ] 

    row: [ 
     opt ending end break 
     | 
     row-feed width [ 
      delimiter [ 
       value emit-value 
       | emit-none 
      ] 
     ] 
    ] 

    func [stream [string!]][ 
     if parse/all stream [header some row][out] 
    ] 
] 
+0

Verdiğim tuhaf veriler üzerinde çalışmak için (şimdiye kadar) görünen bir yanıt üzerinde harika yanıt süresi! – HostileFork

2

yıllar önce bunu yapmak zorunda. Ben bundan beri buldukları tüm vakaları ele almak için benim func güncelledim. Umarım şimdi daha sağlamdır. o AMA içindeki yenisatırlar dizelerle işleyebileceği

Uyarı: dizeleri

  1. yeni satırlar kayıtları arasında
  2. satır CRLF ve olmalı ... sadece ve ŞB edilmelidir ..
  3. Dosyayı read/binary ile yüklemelisiniz, böylece Rebol yeni satırları otomatik olarak dönüştürmez.

Gerekirse, ben muadili block-to-csv var

; Conversion function from CSV format 
csv-to-block: func [ 
    "Convert a string of CSV formated data to a Rebol block. First line is header." 
    csv-data [string!] "CSV data." 
    /separator separ [char!] "Separator to use if different of comma (,)." 
    /without-header "Do not include header in the result." 
    /local out line start end this-string header record value data chars spaces chars-but-space 
    ; CSV format information http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm 
] [ 
    out: copy [] 
    separ: any [separ #","] 

    ; This function handle replacement of dual double-quote by quote while copying substring 
    this-string: func [s e] [replace/all copy/part s e {""} {"}] 
    ; CSV parsing rules 
    header: [(line: copy []) value any [separ value | separ (append line none)] (if not without-header [append/only out line])] 
    record: [(line: copy []) value any [separ value | separ (append line none)] (append/only out line)] 
    value: [any spaces data any spaces (append line this-string start end)] 
    data: [start: some chars-but-space any [some spaces some chars-but-space] end: | #"^"" start: any [some chars | {""} | separ | newline] end: #"^""] 
    chars: complement charset rejoin [ {"} separ newline] 
    spaces: charset exclude { ^-} form separ 
    chars-but-space: exclude chars spaces 

    parse/all csv-data [header any [newline record] any newline end] 
    out 
] 

(1. ve 2. Excel örneğin vermek budur).

[Düzenle] Tamam, muadili (not:! Tüm dize Sonucun bunu isterseniz bloğun ilk satırda olmalıdır çift tırnak ve başlık ile birlikte verilecektir):

block-to-csv: func [ 
    "Convert a block of blocks to a CSV formated string." 
    blk-data [block!] "block of data to convert" 
    /separator separ "Separator to use if different of comma (,)." 
    /local out csv-string record value v 
] [ 
    out: copy "" 
    separ: any [separ #","] 
    ; This function convert a string to a CSV formated one 
    csv-string: func [val] [head insert next copy {""} replace/all replace/all copy val {"} {""} newline #{0A} ] 
    record: [into [some [value (append out separ)]]] 
    value: [set v string! (append out csv-string v) | set v any-type! (append out form v)] 

    parse/all blk-data [any [record (remove back tail out append out crlf)]] 
    out 
] 
+0

Hey, teşekkürler! Aslında bu görev için bir "blok-csv" ye ihtiyacım var, bu yüzden eğer cevabı düzenlemek için onu düzenlemek istiyorsanız, onu yazmak zorunda kalmamı engeller (her ikisinin de daha kolay olmasına rağmen). – HostileFork

İlgili konular