「壊れた」CSV(CSV file)の大半は、実は壊れていません。CSVはテキストファイルで、テキストファイルが劣化することはまずありません。ほとんどの場合、ファイルは書き出した側の前提で作られ、読み込む側が別の前提で開いています。区切り文字が違う、文字コードが違う、書き出しツールが引用符のルールを間違えた、といった具合です。
つまり、修復の前に診断が必要です。症状を見れば、6つの原因のどれなのかが分かり、それぞれに決まった直し方があります。
最短の答え
まず、スプレッドシートで開くのではなく、生のバイト列を確認してください。スプレッドシートは手がかりを隠してしまいます。
head -5 broken.csv # the actual separators and quotes
file -I broken.csv # the real encoding
wc -l broken.csv # how many lines it thinks there are
そのうえで、症状から原因を探します。
| 症状 | 原因 | 対処 |
|---|---|---|
| すべてが1列に入る | 区切り文字が違う | 区切り文字を指定して開き直す |
é、’、ü | UTF-8をLatin-1として読んだ | UTF-8で開き直す |
先頭の見出しに  が付く | バイトオーダーマーク | BOMを取り除く |
| レコードの途中で行が分かれる | フィールド内の改行 | 引用符を直す |
| N行目以降で列数がずれる | エスケープされていない引用符 | N行目を探して直す |
| 行の途中でファイルが終わる | 書き込みが途中で切れた | 再エクスポートする |
1. すべてが1列にまとまってしまう
圧倒的に多いパターンで、ファイルのせいではありません。
ヨーロッパ大陸の多くの地域では、カンマが小数点として使われるため、セミコロンが区切り文字になります。また、拡張子が .csv でも、実際にはタブ区切りの書き出しもたくさんあります。head -5 を実行すれば、どちらかすぐに分かります。
直し方は、読み込む側に区切り文字を教えることです。検索と置換で書き換えてはいけません。セミコロンをカンマに置換すると、引用符で囲まれた値の中のセミコロンまで置き換わり、本当に壊れたファイルになってしまいます。
- Excel:ダブルクリックしないでください。データ → テキストまたはCSVからを選び、プレビューで区切り文字を指定します。
- Googleスプレッドシート:ファイル → インポート → 区切り文字でカスタムを選びます。
- コマンドライン:
duckdb -c "SELECT * FROM read_csv('broken.csv', delim=';')" - スマートフォン:一度だけ推測して諦めるアプリではなく、解析設定を指定してファイルを開き直せるアプリを使います。
区切り文字・ヘッダー・文字コードを自動判定します。推測が外れたときに、解析設定を自分で指定して開き直せる点が、ここでは特に重要です。
できること2. 文字化け:é が é になる
典型的な文字コードの不一致です。ファイルはUTF-8なのに、Latin-1やWindows-1252として読まれています。バイト列は無傷で、解釈のほうが間違っています。
file -I broken.csv
# broken.csv: text/plain; charset=utf-8
直し方:UTF-8を指定して開き直します。Excelのインポート画面にはファイルの元の形式というドロップダウンがあるので、65001: Unicode (UTF-8) を選んでください。コマンドラインでは次のとおりです。
iconv -f WINDOWS-1252 -t UTF-8 broken.csv > fixed.csv
変換の向きに注意してください。ファイルが本当にWindows-1252(古いシステムや一部の基幹システムの出力)なら、このコマンドで変換できます。しかし、すでにUTF-8で、表示だけが間違っている場合は、変換するとかえって悪化します。文字化けした状態をエンコードしてしまうからです。何かを実行する前に、file -I で確認してください。
3. 最初の列名が order_id になる
UTF-8のバイトオーダーマークです。ファイルの先頭に付く3バイトの見えないデータで、Excelや多くのWindows系ツールが書き込みます。スクリプトでの見出しの照合を壊し、単純なパーサーでは文字化けのように表示されます。
sed -i '' '1s/^\xEF\xBB\xBF//' broken.csv # macOS
sed -i '1s/^\xEF\xBB\xBF//' broken.csv # Linux
取り除くのは、後続の処理が引っかかっているときだけにしてください。Excelは実際には、BOMを必要とします。ダブルクリックしたときにExcelがUTF-8のCSVだと判断する手がかりがBOMで、これを消したせいで、ファイルを送った相手のところで日本語やアクセント付きの文字が化けることがあります。
4. 行が途中で分かれてしまう
10,000行のはずが10,340行になり、住所があるべき場所に断片が入っています。住所、コメント欄、貼り付けた段落などが、フィールドの中に改行そのものを含んでいるのです。
フィールドが引用符で囲まれていれば、これは正しいCSVです。
id,name,notes
1,"Alice","Called back.
Wants a refund."
RFC 4180で認められており、きちんとしたパーサーなら問題なく処理できます。問題が起きるのは、ツールが引用符を考慮する前に改行で分割してしまう場合です。多くのシェルのワンライナー、一部のスプレッドシートのインポート機能、自作のコードの多くがこれに当たります。
直すべきなのは、たいていファイルではなく読み込む側です。引用符を理解するパーサーを使ってください。DuckDB、Pythonのcsvモジュール、csvkit、または本物のスプレッドシートのインポートなどです。フィールドが本当に引用符で囲まれていないなら、書き出し自体が壊れているので、修復するより再エクスポートのほうが早いでしょう。
どちらのケースか確かめるには、次を実行します。
csvclean -n broken.csv # reports rows with the wrong field count
5. 4,812行目以降がすべておかしい
途中から列数がずれ、そのまま戻りません。原因はエスケープされていない引用符が1つあることで、それ以降はすべて、閉じられない文字列の中として読まれています。
id,product,price
1,"6"" pipe",4.50
この 6" は 6"" と書くべきでした。実際には、パーサーは値が 6 で終わると見なし、続く " が新しい値を開始して、ファイルの残りがその中に入ってしまいます。
見つけ方:
csvclean -n broken.csv
awk -F',' 'NF != 3 {print NR": "$0}' broken.csv # rough, ignores quoting
確実なのは csvclean です。awk の行は、引用符内にカンマがないファイル向けの簡易的な近似です。行番号が分かったら、その1行だけを手で直します。内側の引用符を二重にするか、in. に置き換えて先へ進みましょう。
スマートフォンでは、特定の行にジャンプして1つのセルだけを編集できるアプリがないと、この作業は面倒です。
ファイル全体を開いて検索できるので、構造が崩れる行を見つけたうえで、修復するか再エクスポートするかを判断できます。
できること6. ファイルが途中で終わっている
最後の行がレコードの途中で切れている、またはファイルサイズが妙にキリのいい数字になっている場合です。書き込みが中断されたか、ダウンロードが中断されています。
tail -3 broken.csv
ここで直せるものはありません。データはそもそも書き込まれていないからです。不完全な最終行を削除して欠損を受け入れるか、再エクスポートしてください。ダウンロードの場合は、ファイルを疑う前に、Content-Length と実際のサイズを比べましょう。
修復のルール
上記のほとんどを問題なく片付けられる、4つの習慣です。
- コピーで作業する。
cp broken.csv broken.backup.csv。このページの対処法は、確認するまではすべて推測です。元に戻せない推測は、割に合いません。 - ファイルより先に、読み込む側を直す。 ここで挙げた症状の多くは設定ミスです。間違ったパーサーに合わせてデータを書き換えると、問題が2つに増えます。
- 区切り文字を検索と置換で変えない。 引用符内の区切り文字まで置き換わり、読めるファイルが壊れたファイルになります。
- 前後で行数を数える。 どの修復でも行います。意図せず数が変わっていたら、修復で何かを壊しています。
諦めて再エクスポートするとき
構造的におかしい行が数行を超えるなら、修復はやめましょう。壊れた書き出しは、たいてい書き出し側の引用符処理にバグがあることを意味します。今は正常に見える行も含めて、そのツールが作るすべての行が疑わしくなります。ファイルをもう一度出してもらい、引用符付きで出力してもらってください。