2012-11-19 40 views
5

Tôi đang cố gắng sử dụng PARSE để chuyển dòng CSV thành khối Rebol. Đủ dễ để viết bằng mã mở, nhưng cũng như với các câu hỏi khác, tôi đang cố gắng tìm hiểu phương ngữ có thể làm gì nếu không có điều đó.Cách sử dụng phương ngữ PARSE để đọc trong một dòng từ CSV?

Vì vậy, nếu một dòng nói:

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

Sau đó, tôi muốn các khối:

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

Các vấn đề cần chú ý:

  • dấu ngoặc kép nhúng trong chuỗi CSV được chỉ báo bằng ""
  • Dấu phẩy có thể nằm trong dấu ngoặc kép và h ence một phần của văn chương, không phải là một dấu phân cách cột
  • dấu phẩy cột tách Liền kề chỉ ra một cánh đồng trống
  • Strings không chứa dấu ngoặc kép hoặc dấu phẩy có thể xuất hiện mà không có dấu ngoặc kép
  • Đối với thời điểm chúng ta có thể giữ cho mọi thứ như http://rebol.com là STRING! thay vì LOAD, hãy nhập chúng vào các loại như URL!

Để làm cho nó đồng nhất hơn, điều đầu tiên tôi làm là gắn dấu phẩy vào dòng nhập. Sau đó, tôi có một số column-rule chụp một cột đơn được chấm dứt bằng dấu phẩy ... có thể có dấu ngoặc kép hay không.

Tôi biết có bao nhiêu cột cần có do dòng tiêu đề, vì vậy các mã sau đó nói:

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

Nhưng tôi là một chút khó khăn trên bằng văn bản column-rule. Tôi cần một cách trong phương ngữ để thể hiện "Khi bạn tìm thấy báo giá, hãy bỏ qua các cặp trích dẫn cho đến khi bạn tìm thấy báo giá tự đứng một mình". Cách tốt nhất để làm điều đó là gì?

Trả lời

3

Giống như hầu hết các vấn đề phân tích cú pháp, tôi cố gắng xây dựng một ngữ pháp mô tả tốt nhất các yếu tố của định dạng đầu vào.

Trong trường hợp này, chúng tôi có danh từ:

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

Một số động từ:

[row-feed emit-value] 

Và những danh từ tác:

[current chunk current-row width] 

Tôi cho rằng tôi có thể có thể phá vỡ nó xuống một nhiều hơn một chút, nhưng đủ để làm việc. Trước tiên, nền tảng:

comma: "," 
ending: "^/" 
qmark: {"} 
value-chars: complement charset reduce [qmark comma ending] 
quoted-chars: complement charset reduce [qmark] 

Bây giờ là cấu trúc giá trị. giá trị trích dẫn được xây dựng từ những khối ký tự hoặc dấu ngoặc kép có giá trị khi chúng ta tìm thấy chúng:

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 
    ) 
] 

Lưu ý rằng delimiter được thiết lập để ending vào đầu mỗi hàng, sau đó đổi thành comma ngay khi chúng tôi vượt qua một giá trị.Do đó, một hàng đầu vào được định nghĩa là [ending value any [comma value]].

Tất cả những gì còn lại là để xác định cấu trúc tài liệu:

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] 

Wrap nó lên để bảo vệ tất cả những lời đó, và bạn có:

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

Thời gian phản hồi tuyệt vời trên câu trả lời dường như (cho đến nay) để làm việc trên dữ liệu lập dị mà tôi đã cung cấp! – HostileFork

2

tôi phải làm điều đó năm trước. Tôi đã cập nhật funcs của mình để xử lý tất cả các trường hợp tôi đã tìm thấy kể từ đó. Tôi hy vọng nó là rắn hơn bây giờ.

Chú ý rằng nó có thể xử lý chuỗi với dòng mới bên NHƯNG:

  1. dòng mới trong chuỗi phải được TĐT chỉ ...
  2. newline giữa hồ sơ phải CRLF ..
  3. bạn phải tải tệp có đọc/nhị phân để Rebol không tự động chuyển đổi dòng mới.

(1. và 2. là những gì Excel cung cấp cho, ví dụ)

; 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 
] 

Nếu cần, tôi có đối tác block-to-csv.

[Chỉnh sửa] OK, đối tác (lưu ý: tất cả các chuỗi sẽ được kèm theo dấu nháy kép và tiêu đề phải nằm trong dòng đầu tiên của khối nếu bạn muốn nó trong kết quả):

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

Xin cảm ơn! Tôi thực sự cần một 'block-to-csv' cho nhiệm vụ này, vì vậy nếu bạn muốn chỉnh sửa câu trả lời để ném nó vào, nó sẽ giữ cho tôi không phải viết nó (mặc dù nó dễ dàng hơn trong hai). – HostileFork

Các vấn đề liên quan