1
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 3 years have passed since last update.

Kinx ライブラリ - CSV Parser

Last updated at Posted at 2020-05-22

CSV

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

今回は CSV パーサーです。TSV もあります。

使い方

using CSV

CSV ライブラリは標準組み込みではないため、using ディレクティブを使用して明示的に読み込む。

using CSV;

全てを一度にパース

ファイルを読み込んで全体をパースするには、CSV.parse() を使う。

using CSV;
var r = CSV.parse("filename.csv");

データの形式は以下の通り。全ての行オブジェクトの配列として返す。行オブジェクトには data フィールドがあり、そこに配列として各要素の文字列が設定される。ただし、コメント行(先頭が #)の場合、data フィールドは空となり comment フィールドにコメント文字列が入る(# も含む)。

[{
    "data": ["aaa", "bbb", "ccc"]
}, ..., {
    "data": {},
    "comment": "# comment"
}]

1行ずつコールバック

行数が多い場合、全体を一気にパースすると大量にメモリを消費する可能性がある。そこで、以下のように 1 行ずつコールバックさせることもできる。コールバックの引数は 1 行分の行オブジェクトとなる。

using CSV;
CSV.parse("filename.csv", &(row) => {
    System.println(row);  // like `{ data: ["aaa", "bbb", "ccc"] }`
});

尚、この場合 CSV.parse() は値を返さない(null を返す)。

文字列をパース

文字列をパースする場合、CSV.parseString() を使う。コールバック版も同様。

using CSV;
var r = CSV.parseString(csvString);
CSV.parseString(csvString, &(row) => {
    ...
});

CSV 仕様

解釈する CSV の仕様は以下の通り。

  • 先頭文字が # の場合、コメント行と認識。
  • ダブルクォートは無くても良い。
  • ,"、改行を含めたい場合はダブルクォートが必要。
  • ダブルクォート内では以下の仕様。
    • , は直接記載可能。
    • """ と重ねて記載。
    • 改行はそのまま改行することで表現する。
  • 最終行の末尾に改行コードは無くても良い。

サンプル

サンプルとして以下をパース。

# comment in CSV.
abcde,efgh,non-quoted string
"abcde","efgh","quoted string"
"abcde",efgh,"quoted & non-quoted string in the same row"
"abcde",efgh,"can use , or "" in csv"
"abcde",efgh,"can use newlines

in the row"
# another comment in CSV.
abcde,efgh,not necessary \n the end of csv

結果はこうなる。

[{
    "data": {},
    "comment": "# comment in CSV."
}, {
    "data": ["abcde", "efgh", "non-quoted string"]
}, {
    "data": ["abcde", "efgh", "quoted string"]
}, {
    "data": ["abcde", "efgh", "quoted & non-quoted string in the same row"]
}, {
    "data": ["abcde", "efgh", "can use , or \" in csv"]
}, {
    "data": ["abcde", "efgh", "can use newlines\n\nin the row"]
}, {
    "data": {},
    "comment": "# another comment in CSV."
}, {
    "data": ["abcde", "efgh", "not necessary \\n the end of csv"]
}]

TSV

TSV とは CSV のカンマ区切りがタブ区切りになったもの。

using CSV は一緒。使うときに CSVTSV にするだけで使える。

using CSV;
var r = TSV.parse(tsvFilename);
TSV.parseString(tsvString, &(row) => {
    ...
});

パーサー

パーサー自体を Kinx で書いています。

パーサーの形態は1文字先読みの再帰下降パーサーですが、そもそも CSV の仕様に再帰的な要素がないので再帰しません。言うなればただの下降パーサーです。

コードは GitHub にあります。全体で100行ちょっとしかないのですぐ理解できるかと思います。

内部ライブラリ用の特殊キーワードについて

ちなみに、_class_function というキーワードが使われていますが、一般では使いません。意味は classfunction と同様です。

何が違うかというと、_ 付きのほうは例外発生時にスタックトレースに情報を残さないといった動作をします(また、-d オプションで VM コードを表示しません)。これは内部ライブラリの場合、スタックトレースに情報を残すと混乱する可能性があるためです。例えば、例外の起点が内部ライブラリの行番号を示していると、そこに問題があると勘違いする可能性があるためで、例外の起点はあくまでユーザー・コードを示すようにしています。VM コード出力でもユーザー・コードの邪魔になると考えられるので表示しないといった配慮がなされる形です。

このストラテジーに従うと逆にライブラリ内部にバグがあった場合は調査困難になりますが、通常ケースでパラメーター異常による例外、といったケースのほうが一般的と考えられるので、この仕組みを入れてあります。尚、内部ライブラリ自体のデバッグをしたい場合は _classclass に、_functionfunction に変更すればできます。

おわりに

CSV パーサーは何か既存のライブラリを使おうかと思って探したんですが、イマイチ統合するのが手間取りそうだったのと、実は簡単なんじゃないかと思ってスクラッチで書いてみました。何か問題があればお知らせください。

ではまた、次回。

1
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
1
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?