テキストファイルのすべての行は何かで終わっており、その「何か」の候補は 3 つあります。どれも目に見えず、互いに置き換えが利かず、1 つのファイルの中で混ざることはよくあり、しかもうっかり起こせます。
気づき方は妙に具体的です。最後の列を除いてすべて動き、その列だけ、どの値も一致を拒みます。
3 つの改行コード
LF— 1 バイト、0x0A。Unix、Linux、そして OS X 以降の macOS。\nと書きます。CRLF— 2 バイト、0x0D 0x0A。Windows のほか、HTTP ヘッダーや電子メールの通信形式でもあります。\r\nと書きます。CSV の仕様である RFC 4180 が指定しているのは CRLF です。 LF が中立な選択だと思っていた人を驚かせる点です。CR— 1 バイト、0x0D単独。バージョン 9 までの古い Mac OS です。いま新しく作ることはありませんが、古い書き出しや一部の旧来のシステムはいまだにこれを吐き、\nでしか分割しないパーサーは、そうしたファイルを 1 本の巨大な行として受け取ります。
どれを使っているかは、ファイルのどこにも書いてありません。見て推測するしかなく、だからこそツールごとに判断が食い違います。
なぜ最後の列だけが壊れるのか
これは覚えておく価値のある症状です。それだけで原因が特定できるからです。
ファイルが CRLF で書かれています。何かがそれを読み、\n だけで行を分割します。行の切れ目としては正しく分かれます——しかし各 \n の直前にあった \r は区切りの一部ではなかったので、行の末尾に付いたまま残ります。その行の最後のフィールドは、いま、何も描かない復帰文字で終わっています。
つまり、最後の列の "active" は、実際には "active\r" です。どの比較にも失敗し、どのログにも同じように出力され、コピーしても生き延びます。これは構造的な原因を持った見えない文字の問題であり、貼り付けの事故を探しても見つかりません。
見分け方は、それが最後の列に、最後の列だけに、すべての行で一様に起きることです。人が貼り付けた空白が、そこまで整っていることはありません。
混在はどうやって起きるのか
1 つのファイルに 2 種類以上が入っている状態は、ツールを最も混乱させます。そして、ごく普通の経路で発生します。
- 別のツールで行が追加された。 元の書き出しは CRLF で書き、スクリプトが 40 行を LF で足し、いま両方が入っています。
- 合わないエディタで編集した。 ファイルの既存の改行コードを保つエディタもあれば、触った行にだけ自分の流儀を当てるエディタもあります。
- 2 つのシステムからの書き出しをマージした。
- セルの中に改行が入っている。 改行を含む引用フィールドは正当なものですが、素朴な判定器はそれをもう一方の流儀の証拠として数えます。これは混在ではまったくないのに、ツールは混在として報告します。
混在は報告する価値はありますが、慌てる価値はめったにありません。直し方は、どちらかを選んで一貫して書き直すことです。ただし意図して選んでください。次にそのファイルを受け取るものに、意見があるかもしれません。
Git と、全部が変わったという差分
ここで午後をまるごと失う最もよくある道筋は、解析とは何の関係もありません。
Windows の Git は core.autocrlf=true の状態で入っていることが多く、これはファイルが作業ディレクトリに置かれるとき LF を CRLF に変換し、コミットするとき LF に戻します。ソースコードに対しては、目に見えず、ありがたい働きです。データファイルに対しては、ディスク上のバイト列とリポジトリの中のバイト列が一致しない、ということを意味します。
症状は、セルを 1 つ編集しただけなのに、50,000 行のファイルの全行が変わったと主張する差分です。実際に変わったのはファイル全体の改行コードで、何かがもう一方の流儀で書き直したからです。
対処は、そのファイルが何であるかを述べる .gitattributes です。
*.tsv text eol=lf
*.csv text eol=lf
あるいは、ファイルをいまあるバイト列のまま保ちたく、Git に一切解釈させたくない場合は、こうします。
*.tsv -text
これはファイルをコミットしたあとではなく、前に設定してください。あとから変えるとファイルがもう一度書き直され、巨大な差分がもう 1 つ生まれます。
どちらかを選ぶ、そして末尾の改行
Unix 系や複数環境にまたがる処理の中で完結するファイルなら、LF です。Windows のソフトに渡すファイルや、Excel で開く同僚に渡すファイルなら、CRLF のほうが安全な推測であり、CSV の仕様が求めているものでもあります。
この隣にもう 1 つ、隠れた判断があります。最後の行を改行で終えるかどうかです。POSIX はテキストファイルの最終行は終端されると定めており、ツールもそれを前提にします。改行で終わっていないファイルは wc -l の数え落としを招きますし、そうしたファイルを 2 つ連結すると、1 つ目の最後の行と 2 つ目の見出し行がくっつきます。
どちらを選ぶかより大事な原則があります。編集しているだけのファイルは、届いたときの改行コードのまま返るべきです。 CRLF を黙って LF に変えてしまう修復は、セル 1 つを直すために 50,000 行を変えたことになり、次にそれを読むものにとってはファイルを壊したのと同じです。
ここでやる場合
このサイトのエディタは、開いた時点で改行コードを判定し、種類ごとに別々に数え、前提を置く代わりにステータスバーへ結果を出します。2 種類以上あればそう伝えますし、本当の混在と、引用符に囲まれたセルの中の改行とを区別します。この 2 つは同じものではありません。
TSV・CSV の書き出しダイアログでは、レコード間の改行を LF または CRLF にするか、読み込み時の設定を使います。読み込んだ CR も引き継げます。末尾の改行の有無も設定できます。改行が統一された入力ではその形式を初期値にし、末尾の改行の有無も引き継ぎます。混在する場合は最も多い形式に統一し、同数なら CRLF、LF、CR の順に優先します。セル内の改行は変えません。たとえば a,b\r\nc,d\ne,f は、レコード間が両方とも CRLF になり、末尾には改行を付けません。引用符や文字コードが変わる場合もあるため、セルを 1 つ直しても、変わる行が 1 行だけとは限りません。
アップロードはありません。ファイルを読み書きしているのは、あなたのブラウザです。