2012-11-19 5 views
5

PARSE를 사용하여 CSV 라인을 Rebol 블록으로 변환하려고합니다. 오픈 코드로 작성하기는 쉽지만 다른 질문과 마찬가지로 방언이 없으면 무엇을 할 수 있는지 배우려고합니다.PARSE 사투리를 사용하여 CSV에서 한 줄을 읽는 방법?

그래서 선이 말한다면 : 통지

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

문제 :

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

가 그럼 난 블록을 원하는

CSV 문자열에서
  • 임베디드 따옴표 ""
  • 로 표시됩니다
  • 쉼표는 따옴표로 묶고 h를 사용할 수 있습니다. 문자 그대로가 아닌 열 구분의 줬어 부분
  • 인접 컬럼 분리 쉼표 따옴표를 포함하지 않는 또는 쉼표 우리가 일을 계속 할 수있는 순간를 들어
  • 따옴표없이 표시 할 수있는 빈 필드를
  • 문자열을 나타냅니다 STRING으로 http://rebol.com을 좋아하세요! 대신 LOAD 같은 URL!

가 더 균일하게하기 위해 유형으로 그들을 보내고, 내가 제일 먼저 입력 라인에 쉼표를 추가합니다. 그렇다면 column-rule은 쉼표로 끝나는 단일 열을 캡처합니다 ... 따옴표로 묶을 수도 있고 그렇지 않을 수도 있습니다.

나는 헤더 행으로 인해이 있어야한다 얼마나 많은 열을 알고, 그래서 코드는 말합니다 :

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

하지만 조금 column-rule 작성에 붙어 있어요. 을 표현하기위한 방언에서의 방법이 필요합니다. "일단 견적을 찾으면 견적을 찾을 때까지 견적 쌍을 건너 뛰십시오." 그 일을하는 좋은 방법은 무엇입니까?

답변

3

대부분의 구문 분석 문제와 마찬가지로 입력 형식의 요소를 가장 잘 설명하는 문법을 작성하려고합니다.

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

일부 동사 :

[row-feed emit-value] 

그리고 수술 명사 : 나는 아마도 그것을 무너 뜨리는 수도있을 것 같군요

[current chunk current-row width] 

이 경우, 우리는 명사가 조금 더 있지만 작업하기에 충분합니다. 첫째, 기초 :

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

이제 가치 구조. 우리가 그들을 찾을 인용 값은 유효한 문자 나 따옴표의 덩어리에서 구축됩니다

delimiter은 각 행의 시작 부분에 ending로 설정하는 것으로
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 
    ) 
] 

후 즉시 우리가 값을 통과 comma로 변경되었습니다.따라서 입력 행은 [ending value any [comma value]]으로 정의됩니다. 남아

모든 문서 구조를 정의하는 것입니다 :

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] 

이 모든 단어를 보호하기 위해 그것을 랩을하고, 당신은 :

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

내가받은 적이있는 이상한 데이터에 대해 (지금까지) 생각한 대답에 대한 환상적인 응답 시간! – HostileFork

2

은 몇 년 전에 그렇게했다. 이후로 찾은 모든 사례를 처리 할 수 ​​있도록 기능을 업데이트했습니다. 나는 그것이 지금 더 견고하기를 바란다. 이 하지만 내에서 개행 문자와 문자열을 처리 할 수있는

공지 사항 : 문자열의

  1. 줄 바꿈 레코드 사이
  2. 줄 바꿈은 CRLF 해야합니다 ... 만 를 LF해야합니다 ..
  3. Rebol은 자동으로 개행 문자를 변환하지 않으므로 파일을 읽기/이진 파일로로드해야합니다.

필요한 경우, 내가 상대 block-to-csv

; 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.과 2. 엑셀, 예를 들어주는 것입니다).

[편집] OK, 상대 (주의! 모든 문자열은 당신이 결과에서 원하는 경우 블록의 첫 번째 줄에 있어야합니다 따옴표와 헤더에 첨부되어있을 것입니다) :

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

안녕하세요, 감사합니다. 실제로이 작업을 위해'block-to-csv'가 필요합니다. 만약 당신이 그 질문을 던지기위한 답을 편집하고 싶다면, 그것을 쓰지 않아도됩니다. (둘 중 더 쉬울지라도). – HostileFork