これはなに
最近,仕事で外部の部署からもらったCSV形式のデータファイルをデータベースにインポートする機会があったのですが,インポートをする際,幾度となくフォーマットエラーにハマってしまいました…
一口に「CSVファイル」と言っても,人によって思い浮かべるものが違うらしく,そこら辺の認識齟齬が原因になっていたようです.
ここでは,すでに存在するCSV/TSVファイルをデータベースソフトウェアにインポートする際に,気をつけておきたいフォーマット上の点と,前処理の方法についてまとめます.
環境はOS X 10.11ですが,基本的にUnixコマンドが使える環境であれば問題なく適用できる知識かと思います(Windowsでは文字コード,改行コード回りの操作が若干異なる可能性があります).
以下,カレントディレクトリにdata1.csv,data2.tsvの2ファイルが存在することを仮定します.
文字コードと改行コード
文字/改行コードを確かめる
まず最初に,ファイルの文字コードと改行コードを確かめましょう.
特に,ファイル中に日本語が含まれる場合は,インポートする際に文字コード変換が必要となる可能性があるため,重要な要素になります.
nkf
コマンドが利用できる環境であれば,
nkf --guess data1.csv
とすることで,ファイルの文字コードと改行コードを自動判定してくれます.
文字/改行コードを変換する
前項で判定された文字/改行コードによって,今後の処理が変わります.
-
"ASCII (LF)"と表示されれば,マルチバイト文字が含まれておらず,かつ改行コードがLFなので問題ありません.次のステップに進んでください.
-
"BINARY"と表示された場合,複数の文字コードとして解釈されるバイト列が含まれているか,そもそもテキストファイルとして不正なバイト列が含まれている可能性があります.必要に応じて,Emacsなど,バイト列を編集できるエディタで該当部分を削除した上で,もう一度
nkf --guess
を繰り返してください. -
それ以外の内容が表示された場合は,必要に応じて文字/改行コードの変換を行いましょう.例えば,"UTF-8 (LF)"と表示された場合,UTF-8でエンコーディングされたマルチバイト文字が存在します.
例えば,この文字コードをShift-JIS,改行コードをLFに変換したい場合,nkfを用いて,
nkf -s -x -Lu data1.csv > data1_sjis.csv
# data1.csvの文字コードをShift-JIS,改行コードをLFに変換したものをdata1_sjis.csvとして新規に作成
のようにできます.
具体的には,
オプション | 操作 |
---|---|
-j | 文字コードをISO-2022-JPに変換 |
-s | 文字コードをShift-JISに変換 |
-e | 文字コードをEUC-JPに変換 |
-w | 文字コードをBOMなしUTF-8に変換 |
-Lu | 改行コードをLFに変換 |
-Lw | 改行コードをCR+LFに変換 |
-x | 半角カナを全角カナに変換せずそのまま出力 |
のようにオプションを指定して文字/改行コードを変換します.
多くの場合,文字コードをUTF-8,改行コードをLFとしておけば十分ですが,MS Excelへのインポートについては,Excelが基本的にShift-JISしか受け付けない(それ以外だと文字化けしてしまう)ことに注意しておく必要があります.
ヘッダとフォーマット
前項までで,「テキストファイルとして妥当かどうか」についてのバリデーションは完了しました.
次に,「CSV/TSVファイルとして妥当かどうか」についてのバリデーションに移ります.
以下の4点について確認します.
該当ファイルのフォーマットが仕様書等で規定されているか
明示的に仕様書がある場合はそれを参照すればいいですが,ない場合についても,想定されるカラムの総数と各カラムのフォーマット(数値か文字列か,文字列ならそれが時刻を表しているかなど)をこの時点である程度確認しておくとよいかと思います.
といっても,データ量が大きい場合,すべて目視で確認するのは不可能なので,実際にインポートしてみてエラーを検知したら修正…という方法を取らざるを得ないとは思います.
該当ファイルがヘッダ行を含んでいるか
多くのデータベースソフトウェアでは,ヘッダ行があればそれをベースとしてインポートする際のスキーマを自動的に規定する機能がありますし,いろいろ便利なので,これから新しくCSV/TSVを作る場合は,ヘッダ行を用意しておくとよいかと思います(もちろん,ヘッダ行そのものはインポートしないように気をつける必要はあります).
該当ファイルのフィールド区切りは何か.また,該当ファイルの各カラムがクオートされているか
フィールド区切りについては,",","\t"(タブ文字),"|"などが使われることが多く,クオートについては,ダブルクオーテーションかシングルクオーテーションのどちらかが使われるか,クオートされないのが一般的なようです.
head
などで該当ファイルの一部をざっと眺めて,これらが想定通りか確認しておきましょう.
特に,
column_a,column_b,column_c # ヘッダ行(クオートなし)
a1,b1,c1 # 正常にインポートできる行
a2,Com,ma,c2 # ","がデータ部分に含まれている
"column_a","column_b","column_c" # ヘッダ行(クオートあり)
"a1","b1","c1" # 正常にインポートできる行
"a2","Double"Quote","c2" # ダブルクオーテーションがデータ部分に含まれている
のように,フィールド区切りやクオートの文字列と同じ文字列がデータ部分に含まれている際は非常に厄介です.
このような行を見つけてしまった際には,該当部分の文字列エスケープをしての該当ファイルの再作成を検討するか,該当カラムそのものの削除を検討しましょう.
個人的には,フィールド区切りは"\t"(つまり,TSVファイル)にしておき,データが日本語を含み得る場合は,ダブルクオーテーションでのクオートを行った上で,データ部分に含まれているダブルクオーテーションは,事前に"\""のようにエスケープするのがよいかと思います(データがASCII文字のみの場合はクオート不要だと思います).
データ量
インポートの前に,ファイルのデータ量についても気にしておく必要があります.
この辺はインポートするソフトウェアに依存すると思いますが,これらのソフトはメモリにデータを読み込むため,非常に大きなデータを一度に読み込もうとすると,多くの場合メモリ不足に陥ってしまいます.
ソフトウェアの仕様書を読んだ上で,適宜ファイルを分割してみるとよいでしょう.
ファイルの分割には,split
コマンドが便利です.
以下では,入力ファイルを,<prefix(デフォルトは"x")>[a-z][a-z]
の形式で,指定行ごとに分割しています.
split -l 1000000 data2.tsv out. # ファイルを1,000,000行ごとに分割し,out.*に出力
ls -l out.*
# out.aa (これがdata2.tsvの先頭1,000,000行になる)
# out.ab
# ...
おまけ
実は,CSVにはRFCに記載された仕様が存在します.
将来的なことを考えるとこちらに従うのが一番なのでしょうが,実際のソフトウェアを見てみると,すべての仕様に対応できていないものが多いのが現実です.
まとめ
RFCに準拠してくれとは言わないまでも,ちゃんと一貫したフォーマットのファイルを用意してもらいたいものです…