|
84 | 84 | (define default-tsv-grammar
|
85 | 85 | (csv-grammar '((separator-chars #\tab) (quote-char . #f))))
|
86 | 86 |
|
| 87 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 88 | + |
87 | 89 | ;;> \section{CSV Parsers}
|
88 | 90 |
|
89 | 91 | ;;> Parsers are low-level utilities to perform operations on records a
|
|
375 | 377 | (opt-lambda ((in (current-input-port)))
|
376 | 378 | (cons '*TOP*
|
377 | 379 | (csv->list (csv-read->sxml row-name column-names parser) in)))))
|
| 380 | + |
| 381 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 382 | + |
| 383 | +;;> \section{CSV Writers} |
| 384 | + |
| 385 | +(define (write->string obj) |
| 386 | + (let ((out (open-output-string))) |
| 387 | + (write obj out) |
| 388 | + (get-output-string out))) |
| 389 | + |
| 390 | +(define (csv-grammar-char-needs-quoting? grammar ch) |
| 391 | + (or (eqv? ch (csv-grammar-quote-char grammar)) |
| 392 | + (eqv? ch (csv-grammar-escape-char grammar)) |
| 393 | + (memv ch (csv-grammar-separator-chars grammar)) |
| 394 | + (eqv? ch (csv-grammar-record-separator grammar)) |
| 395 | + (memv ch '(#\newline #\return)))) |
| 396 | + |
| 397 | +(define (csv-write-quoted obj out grammar) |
| 398 | + (let ((in (open-input-string (if (string? obj) obj (write->string obj))))) |
| 399 | + (write-char (csv-grammar-quote-char grammar) out) |
| 400 | + (let lp () |
| 401 | + (let ((ch (read-char in))) |
| 402 | + (cond |
| 403 | + ((eof-object? ch)) |
| 404 | + ((or (eqv? ch (csv-grammar-quote-char grammar)) |
| 405 | + (eqv? ch (csv-grammar-escape-char grammar))) |
| 406 | + (cond |
| 407 | + ((and (csv-grammar-quote-doubling-escapes? grammar) |
| 408 | + (eqv? ch (csv-grammar-quote-char grammar))) |
| 409 | + (write-char ch out)) |
| 410 | + ((csv-grammar-escape-char grammar) |
| 411 | + => (lambda (esc) (write-char esc out))) |
| 412 | + (else (error "no quote defined for" ch grammar))) |
| 413 | + (write-char ch out) |
| 414 | + (lp)) |
| 415 | + (else |
| 416 | + (write-char ch out) |
| 417 | + (lp))))) |
| 418 | + (write-char (csv-grammar-quote-char grammar) out))) |
| 419 | + |
| 420 | +(define csv-writer |
| 421 | + (opt-lambda ((grammar default-csv-grammar)) |
| 422 | + (opt-lambda (row (out (current-output-port))) |
| 423 | + (let lp ((ls row) (first? #t)) |
| 424 | + (when (pair? ls) |
| 425 | + (unless first? |
| 426 | + (write-char (car (csv-grammar-separator-chars grammar)) out)) |
| 427 | + (if (or (and (csv-grammar-quote-non-numeric? grammar) |
| 428 | + (not (number? (car ls)))) |
| 429 | + (and (string? (car ls)) |
| 430 | + (string-any |
| 431 | + (lambda (ch) (csv-grammar-char-needs-quoting? grammar ch)) |
| 432 | + (car ls))) |
| 433 | + (and (not (string? (car ls))) |
| 434 | + (not (number? (car ls))) |
| 435 | + (not (symbol? (car ls))))) |
| 436 | + (csv-write-quoted (car ls) out grammar) |
| 437 | + (display (car ls) out)) |
| 438 | + (lp (cdr ls) #f))) |
| 439 | + (write-string |
| 440 | + (case (csv-grammar-record-separator grammar) |
| 441 | + ((crlf) "\r\n") |
| 442 | + ((lf lax) "\n") |
| 443 | + ((cr) "\r") |
| 444 | + (else (string (csv-grammar-record-separator grammar)))) |
| 445 | + out)))) |
| 446 | + |
| 447 | +(define csv-write |
| 448 | + (opt-lambda ((writer (csv-writer))) |
| 449 | + (opt-lambda (rows (out (current-output-port))) |
| 450 | + (for-each |
| 451 | + (lambda (row) (writer row out)) |
| 452 | + rows)))) |
0 commit comments