0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

2015年11月21日: Scheme、Import-Csv

Posted at

2015年11月21日: Scheme、Import-Csv

2015年11月21日 10時28分

PowerShell に Import-Csv とゆう便利なコマンドがあると聞いて、Scheme 用に作ってみた。Gauche とかだと標準ライブラリにあるぽいけど、自分、Gauche 環境ぢゃないし、お仕事で使ってる SKILL にもそんな便利機能はないから、どっちにしても自作しよぉとおもた。

まず Scheme で作ったんで、あとはこれを SKILL に翻訳すれば仕事でも使える。あと、もちろん、Scheme でシェル芸するのにもお役立ち。ご本家の PowerShell だとデリミタを指定するオプションがあるぽい。それもできるようにすればもっといいかも。

ざっくりな方針

  • ダブルクオートの処理とか空白文字の処理とかは Microsoft Excel の処理を基準にする。(PowerShell の Import-Csv をまねると言っておきながら何だけど…)
  • 出力結果は〈〈キーと値のタプル〉のリストのリスト〉とする。これは PowerShell の Import-Csv に倣った。実際その方がその後の処理がし易いしねっ。
  • ヘッダ行のフィールド数と本体のフィールド数が違う場合の処理も当然 PowerShell の Import-Csv を基準にする。
  • CRLFCR-LF のどの改行方式でも対応できるようにする。
  • 基本機能だし、大量のデータが来てもダイジョブなよおに、効率優先の実装にする。
  • 関数としては Scheme 組み込みの以外のものは使わない。

コード

(define (Import-Csv inputPort)
  (if (eof-object? (peek-char inputPort))
    (list)
; else
    (let*
      (
      (head/body
        (let*
          (
          (chars   (list #f)) (chars_last   chars  )
          (fields  (list #f)) (fields_last  fields )
          (records (list #f)) (records_last records)
          )
          (let parseLine ((c (read-char inputPort)))
            (cond
              ((eof-object? c)
                (if (or (pair? (cdr chars)) (pair? (cdr fields)))
                  (begin
                    (set-cdr! fields_last (list (list->string (cdr chars))))
                    (set-cdr! records_last (list (cdr fields)))))
                (cdr records))

              ((char=? c #\return)
                (let ((nextChar (peek-char inputPort)))
                  (cond
                    ((eof-object?      nextChar) #f)
                    ((char=? #\newline nextChar) (read-char inputPort)))
                  (if (or (pair? (cdr chars)) (pair? (cdr fields)))
                    (begin
                      (set-cdr! fields_last (list (list->string (cdr chars))))
                      (set-cdr! records_last (list (cdr fields)))
                      (set!     records_last (cdr records_last))))
                  (set-cdr! fields '()) (set! fields_last fields)
                  (set-cdr! chars  '()) (set! chars_last  chars )
                  (parseLine (read-char inputPort))))

              ((char=? c #\newline)
                (if (or (pair? (cdr chars)) (pair? (cdr fields)))
                  (begin
                    (set-cdr! fields_last (list (list->string (cdr chars))))
                    (set-cdr! records_last (list (cdr fields)))
                    (set!     records_last (cdr records_last))))
                (set-cdr! fields '()) (set! fields_last fields)
                (set-cdr! chars  '()) (set! chars_last  chars )
                (parseLine (read-char inputPort)))

              ((char=? c #\")
                (if (null? (cdr chars))
                  (let parseQuoted ((c (read-char inputPort)))
                    (cond
                      ((eof-object? c)
                        (if (or (pair? (cdr chars)) (pair? (cdr fields)))
                          (begin
                            (set-cdr! fields_last (list (list->string (cdr chars))))
                            (set-cdr! records_last (list (cdr fields)))))
                        (cdr records))
                      ((char=? c #\")
                        (case (peek-char inputPort)
                          ((#\")
                            (read-char inputPort)
                            (set-cdr! chars_last (list #\"))
                            (set!     chars_last (cdr chars_last))
                            (parseQuoted (read-char inputPort)))
                          ((#\,) (parseLine (read-char inputPort)))
                          (else (parseQuoted (read-char inputPort)))))
                      (else
                        (set-cdr! chars_last (list c))
                        (set!     chars_last (cdr chars_last))
                        (parseQuoted (read-char inputPort)))))

                ; else
                  (begin
                    (set-cdr! chars_last (list c))
                    (set!     chars_last (cdr chars_last))
                    (parseLine (read-char inputPort)))))

              ((char=? c #\,)
                (set-cdr! fields_last (list (list->string (cdr chars))))
                (set!     fields_last (cdr fields_last))
                (set-cdr! chars '()) (set! chars_last chars)
                (parseLine (read-char inputPort)))

              (else
                (set-cdr! chars_last (list c))
                (set!     chars_last (cdr chars_last))
                (parseLine (read-char inputPort)))))))

      (head (car head/body))
      (body (cdr head/body))
      )

      (map
        (lambda (values)
          (let* ((buffer (list #f)) (buffer_last buffer))
            (let keyValue ((ks head) (vs values))
              (cond
                ((null? ks) (cdr buffer))
                ((null? vs)
                  (set-cdr! buffer_last (list (list (car ks) "")))
                  (set!     buffer_last (cdr buffer_last))
                  (keyValue (cdr ks) '()))
                (else
                  (set-cdr! buffer_last (list (list (car ks) (car vs))))
                  (set!     buffer_last (cdr buffer_last))
                  (keyValue (cdr ks) (cdr vs)))))))
        body))))

使用例

(let*
  (
  (inputPort
    (open-input-string ; SRFI-6
      (string-append
        "name,company,address,city\n"
        "\"LINCOLN, Abraham\",\"Benton, John B Jr\",6649 N Blue Gum St,New Orleans\n"
        "\"CARTER, Jimmy\",\"Chanay, Jeffrey A Esq\",4 B Blue Ridge Blvd,Brighton\n"
        "\"ROOSEVELT, Franklin\",\"Chemel, James L Cpa\",8 W Cerritos Ave #54,Bridgeport\n"
        "\"BUSH, George\",Feltz Printing Service,639 Main St,Anchorage\n"
        "\"CLINTON, Bill\",Printing Dimensions,34 Center St,Hamilton\n"
        "\"OBARAM, Barack\",\"Chapman, Ross E Esq\",3 Mcauley Dr,Ashland\n")))
  (csv (Import-Csv inputPort))
  )
  (close-port inputPort)
  csv)

 (
  (("name" "LINCOLN, Abraham") ("company" "Benton, John B Jr") ("address" "6649 N Blue Gum St") ("city" "New Orleans"))
  (("name" "CARTER, Jimmy") ("company" "Chanay, Jeffrey A Esq") ("address" "4 B Blue Ridge Blvd") ("city" "Brighton"))
  (("name" "ROOSEVELT, Franklin") ("company" "Chemel, James L Cpa") ("address" "8 W Cerritos Ave #54") ("city" "Bridgeport"))
  (("name" "BUSH, George") ("company" "Feltz Printing Service") ("address" "639 Main St") ("city" "Anchorage"))
  (("name" "CLINTON, Bill") ("company" "Printing Dimensions") ("address" "34 Center St") ("city" "Hamilton"))
  (("name" "OBARAM, Barack") ("company" "Chapman, Ross E Esq") ("address" "3 Mcauley Dr") ("city" "Ashland"))
  )
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?