始めに
今まで数GBのようなある程度大きいCSVを処理したい時には、awkを使うことがそれなりにあった
理想的には、スプレッドシートで済ましたいけど、結構重くなって、場合によっては処理できないこともある
なので、頻度はそこまで多くないけど、必要な時にサッと使いたい機能をメモしておこう
# 例. 空文字でない該当列の値を出力
cat file.csv | awk 'BEGIN{FS=","} {if($6!=""&&$7!=""){print $6,$7}}' | less
と思ったのだけど、awkは複雑なCSVを厳密にパースするのには向いていない
gawkの開発は「完全を目指すのではなく9割をサクサクこなし、フィールドに改行を含むようなCSVファイルは専用のツールで処理すれば良い」という思想で進められました。
上記制限を許容した上で利用する分には良いけれど、、と思っていたところ、他にも色々手段がある
紹介されている中で最も定番ぽい印象を受けた qコマンド について、調べたことを簡単にメモしておく
前提
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H15
$ q -v
q version 3.1.6
Python: 3.9.7 (default, Oct 18 2021, 00:59:13) // [Clang 13.0.0 ]
Copyright (C) 2012-2021 Harel Ben-Attia (harelba@gmail.com, @harelba on twitter)
http://harelba.github.io/q/
まずはインストールしましょう
使い方
基本的には、以下のフォーマットで使っている
$ nkf -w file.csv | q -H -d, -O -T -q ./test.sql | less
# 補足
# 文字コード変換する必要がなければ、`nkf` する必要なし
# ヘッダーがない場合は、`-H` をなくして SQL のカラムは c1, c2, ... を使う
# SQL を直接指定する場合は、`-q ./test.sql` をなくす(例. "select * from -")
入力
- 基本的には、
qコマンド
に入力する前に別ツールで文字コード変換している- 文字コード変換用のオプションがある
- けれど、手元で試した時に上手く読み込めなかった(詳細は後述)
オプション
- ヘッダー読み込み
-H
- 区切り文字指定
- カンマ:
-d,
- タブ:
-t
- カンマ:
- ヘッダー出力
-
-O
- ヘッダー変換したい場合は、上記を指定した上で AS 句を使う
-
- タブ区切り出力
-T
- SQLファイル実行
-
-q [ファイルパス]
- ファイルに記載するSQLはクォートで囲む必要なし
-
- 文字コード指定
-e data-encoding
-Q query-encoding
-E output-encoding
SQL
-
SQLite3 に準拠しているとのこと
-
いくつか制限がある
- サブクエリをfromに置けないとか(whereには書ける)
- 句
group by
,having
,join
, ... - 関数
count
,sum
,substr
, ...
-
いくつか制限がある
- from 指定
- 標準入力:
-
- ファイル:
[ファイルパス]
- 標準入力:
- カラム名
- ヘッダー行あれば(
-H
指定) 、ヘッダー名 - なければ、c1, c2, ...
- ヘッダー行あれば(
補足
CSV のパース
いくつかバリエーションを試したが、特に問題なし
# フィールド内にカンマを含むケース
$ echo 'aaa,"bbb,ccc",ddd' | q -d, "select c2 from -"
=>
"bbb,ccc"
# フィールド内に改行を含むケース
$ echo -e "aaa,\"bbb\nccc\",ddd" | q -d, "select c2 from -"
=>
"bbb
ccc"
# フィールド内にダブルクオートそのものを含むケース
$ echo 'a,"b""c",d' | q -d, "select c2 from -"
=>
"b""c"
# フィールド内にエスケープされていない二重引用符を含むケース
# ※ 不正CSV(RFC 4180 に違反)
$ echo 'a,b"c,d' | q -d, "select c2 from -"
=>
"b""c"
文字コードの変換
Shift_JIS の CSV を読み込もうとしたらエラー
※ 日本語を含む 250MB 程のファイル
$ nkf -guess file.csv
Shift_JIS
$ cat file.csv | q -H -d, -e Shift_JIS -Q UTF-8 -E UTF-8 "select c1, count(1) from -" | less
=>
Could not parse the input. Please make sure to set the proper -w input-wrapping parameter for your input, and that you use the proper input encoding (-e). Error: 'utf-8' codec can't decode byte 0xca in position 293: invalid continuation byte
他のツールだと問題なく読み込める
# 例. Ruby
CSV.read('file.csv', encoding: 'Shift_JIS:UTF-8')
日本語を含むファイルの文字コード変換は、他ツールを使った方が無難?
$ nkf -w file.csv | q -H -d, "select カラム1 from -" | less
終わりに
簡単に使い方を調べて、qコマンドの便利さを理解できたので、実際に使っていこうと思う(データ量が多い場合には、Athena を使えば事足りそう)
類似ツールは Go が多いようで早そうだけど、現状特に課題感はなくて当分は良さそう
後、今回のような些細な記事一つ取っても、改めて視野を広げて取り組まないといけないなと