CSVというのは単一の仕様というのはどうやら無いらしく(RFC4180というのはあるがこれはカテゴリがInformationalなので、標準ではない)、データを吐く側と食う側でどのみち合意が必要なようです。
逆に合意さえあれば、パースを簡略化できます。
そんなわけでzaimのCSVの仕様を調べます。
zaimのCSVの仕様
タブの扱い
pbcopy
(macOSのコマンドです)でタブ文字をクリップボードにコピーして、zaim web からタブ文字を含む取引を登録してみました。
$ printf '\t' | pbcopy
CSVをダウンロードしてみると、タブ文字はそのまま含まれていました。
改行の扱い
zaim Web からは改行文字の入力ができなかったので、アプリから入力してみました。
CSVをダウンロードしてみると、この取引のレコードは、改行がスペースに置き換えられていました。
awkで処理してみる
まず、タブを消す前処理です。
{
sub("\t", " ", $0);
print $0
}
次に、カンマをタブに置換します。
単にtr ',' '\t'
としてしまうと、ダブルクォートの中にカンマが出現する場合、フィールドの区切りがおかしくなってしまいます。
結構悩んだ挙げ句、以下のStackOverflowの回答にたどり着きました。
ダブルクォートでエスケープされているまずダブルクォートを区切り文字としてフィールドを分けてから、1フィールド飛ばしで ,
を \t
に置換するとよいようです。
CSVにカンマが出現する場合、1フィールド中のカンマは必ず偶数個出現することを利用しているわけですね。
BEGIN {
FS=OFS="\""
}
{
for (i=1; i<=NF; i+=2) {
gsub(/,/,"\t", $i);
}
print $0;
}
csvformat を使う
csvkitに含まれているcsvformatコマンドを使うともっと簡単にできます。
brew install csvkit
cat zaim.csv | csvformat -T
cat zaim.csv | csvformat -T | awk -v FS='\t' '{if(NF != 16){print NF}}' # 各行のカラムの数を数えて正しくパースできていることを確認