落ちたのは8342119行目。
分かっているのは、それだけです。ファイルは12GB、移行の検収は明後日。その1行を、いま見に行けるでしょうか。
行番号は、住所ではありません。
データ検品は、障害対応と似ているようで別の仕事です。障害は原因が分かれば終わりますが、検品は「全部見た」と言えるところまで行かなければ終わりません。しかも移行案件の検品は数日にまたがり、途中で中断し、翌日また同じファイルを開くところから始まります。
4つの場面を並べます。パーサが落ちた行の特定・数億行の全量検品・重複の判定・改行コード混在。作業はばらばらですが、4つとも詰まるのは同じ1点です。
- 先に結論: UwView は行番号を指定すればその行へ跳んで前後を原本のまま読め、UwView Pro の Edit Upgrade なら修正を差分ファイル(.ewvz)に積むので、数十GBの原本を書き換えずに直し、途中でやめて翌日続きから再開できます(開発中の計測で10GB・1億行のファイルに対する21,994箇所の一括置換が16.8秒。特定環境での測定例で、環境により異なります。詳細は記事末尾)
- 1. 行番号は分かっている。その行が見られない
- 2. 「全部見ました」と言えるところまで行く
- 3. 重複が12万件。それが「消していい重複」か分からない
- 4. 行数が3つとも違う
- 4つに共通していたもの
- 使っている道具
- リンク
先に結論: UwView は行番号を指定すればその行へ跳んで前後を原本のまま読め、UwView Pro の Edit Upgrade なら修正を差分ファイル(.ewvz)に積むので、数十GBの原本を書き換えずに直し、途中でやめて翌日続きから再開できます(開発中の計測で10GB・1億行のファイルに対する21,994箇所の一括置換が16.8秒。特定環境での測定例で、環境により異なります。詳細は記事末尾)
1. 行番号は分かっている。その行が見られない
状況
移行用のCSVを読み込ませたら、こう言って止まりました。
ParserError: Error tokenizing data. C error: Expected 12 fields in line 8342119, saw 14
ファイルは12GB、約4200万行。エラーは親切です。行番号も、期待した列数も、実際の列数も教えてくれています。それでも、その行が見られません。
なぜ起きるか
行番号が、ファイルの中の位置と直結していないからです。
テキストファイルには目次がありません。8342119行目に辿り着くには、先頭から改行を8342118回数えるしかない。12GBならその走査が毎回走ります。「その行を見る」という最小の操作が、ファイルサイズなりの時間を要求してきます。
そしてもう一段やっかいなのは、その行番号が正しいとは限らないことです。CSVはクォートの中に改行を書けます。"備考","2026年4月から\n適用" のような値が1つあれば、パーサの数える論理行と、wc -l が数える物理行はそこから先ずっとズレます。 パーサが「8342119行目」と言うとき、それが物理的な8342119行目である保証はありません。壊れているのは、たいていその行ではなく、その手前です。
さらに、検品作業として見ると本題はここからです。1件直して再実行すると、次の壊れた行で落ちます。1回の実行で1件しか分からないので、これは数日かけたモグラ叩きになります。
汎用ツールでの対処と限界
その行を切り出すのが定石です。
sed -n '8342119p' migration.csv # その1行
sed -n '8342100,8342140p' migration.csv # 前後も
awk 'NR>=8342100 && NR<=8342140' migration.csv
grep -c '"' migration.csv # クォートは何個あるか
書けば1行です。詰まるのはそこではありません。
限界は3つ。ひとつめ、幅を変えるたびに走査をやり直すことです。 40行では足りず前後200行に広げる、という判断は開いてみるまで下せません。にもかかわらず sed は開く前に範囲を決めろと言い、決め直すたびに先頭から数え直します。第10回で書いた「開き直しの待ち」が、ここでは1件の調査のなかで何度も出てきます。
ふたつめ、論理行と物理行のズレを埋める手段がないことです。 sed は物理行しか知りません。パーサの言う行番号で切り出した結果が的外れだったとき、ではどこを見ればいいのかを教えてくれるものは、この手順の中にありません。
みっつめ、直す段になると、12GBを書き直すことになる点です。sed -i も awk からの書き出しも、やることは全体の再生成です。1文字の修正に、ファイルサイズなりの時間とディスクを使います。しかも書き直しの途中で落ちれば、原本と新ファイルのどちらが正しいかを確かめるところからやり直しになります。検品の失敗コストが高いのは、この最後の一歩のせいです。
2. 「全部見ました」と言えるところまで行く
状況
数億行の移行データを検収します。壊れ方の傾向が読めないので、まず先頭1000行・末尾1000行・ランダム抽出1万行を見ました。
報告に出したら、こう返ってきました。「それで全部だと言えますか。」
なぜ起きるか
壊れ方が、一様に散らばっていないからです。
移行データの異常は、たいてい固まって出ます。旧システムのある時期だけ、あるベンダーから来たバッチだけ、ある文字を含むレコードだけ。原因が1つなら、影響範囲も1箇所に固まります。
ランダムサンプリングは「薄く広く分布する異常」を捕まえる設計です。数億行から1万行を抜くと抽出率はごくわずかで、1万行に固まっている異常は、まるごと網の目を抜けます。 サンプリングで出なかったことは「無い」の証拠になりません。統計的な品質保証としては成立しても、「この1件が漏れたら再移行」という検収では通らない場面があります。
そして検品は、1日で終わりません。数億行を条件を変えながら何度も走査し、途中で日をまたぎます。翌日、どこまで確認したかを思い出すところから始まります。
汎用ツールでの対処と限界
抜いて、眺めて、条件を変えるのが定石です。
awk 'NR%1000==0' huge.tsv | less # 1000行ごと
shuf -n 10000 huge.tsv > sample.tsv # ランダム抽出
awk -F'\t' 'NF!=18 {print NR": "NF}' huge.tsv # 列数が違う行の行番号
awk -F'\t' '$7 !~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ {print NR}' huge.tsv | head -50
3本目と4本目は良い手です。全量を機械にかけて、行番号のリストを得ています。
限界は3つ。ひとつめ、得られるのが行番号のリストで止まることです。 「12万行が該当」と出ても、それが本当に異常なのか、こちらの判定条件が雑なだけなのかは、現物を見るまで分かりません。そして現物を見るには、第1章の問題が12万回待っています。
ふたつめ、見た範囲が記録に残らないことです。 less で眺めた場所も、打った検索条件も、端末を閉じれば消えます。翌日の自分は、昨日と同じ条件を思い出して打ち直すところから始めます。検収資料に「どの条件でどこまで確認したか」を書く段になって、もう一度全部やり直した、という話は珍しくありません。
みっつめ、開き直しの回数がそのまま日数になることです。 検品は「条件を変えて何度も入り直す」作業そのものです。第4回で扱った「残したデータ」が、ここでは開くたびに同じ待ちを課してくる相手になります。
3. 重複が12万件。それが「消していい重複」か分からない
状況
マスタデータの重複チェックです。ソートして uniq -d を通したら、12万件返ってきました。
件数は出ました。ここから先が進みません。
なぜ起きるか
重複かどうかを決めるのは、業務ルールだからです。
機械が判定できるのは一致だけです。実際のデータには少なくとも3種類が混ざります。完全に同じ行が2本ある本物の重複。キーは同じだが中身が違う行(どちらが正なのかは人が決める)。そして、キーも中身も同じだが業務上は正しく複数存在する行——履歴テーブルや多対多の中間テーブルでは普通のことです。
内訳を決めるのは人間の仕事で、その判断は根拠つきで残さなければなりません。検収では「12万件を重複と判定し、うち8万件を削除、4万件は正常と判断した。判断基準は以下」まで書いて初めて通ります。
ここに、巨大ファイル特有の制約が乗ります。sort を通した時点で、元の並びが失われています。 「この重複は原本の何行目と何行目か」に答えられません。削除の指示書は原本の座標で書く必要があるのに、その座標が手元にないという状態です。
汎用ツールでの対処と限界
ソートして数えるのが定石です。
sort -t, -k1,1 master.csv | uniq -d -f0 | wc -l # 重複の件数
awk -F, 'seen[$1]++ {print NR": "$0}' master.csv # 2件目以降を行番号つきで
sort -t, -k1,1 master.csv | uniq -c | sort -rn | head -20
2本目は良い形です。sort を通さずに awk の連想配列で数えているので、原本の行番号が保たれています。
限界は3つ。ひとつめ、メモリに乗るかどうかで手が変わることです。 awk の連想配列はキーの種類数ぶんメモリを使います。数億行・数千万種のキーなら、そこで落ちます。落ちれば sort に戻り、sort に戻れば行番号が失われます。
ふたつめ、sort が一時領域を要求することです。 数億行のソートは入力とほぼ同じ大きさの作業領域を /tmp に取ります。作業サーバで No space left on device に当たるのは、検品作業でよく見る足止めです。
みっつめ、判断の結果を書き戻せないことです。 12万件を仕分けたあと、その判断を数十GBの原本に反映する作業が最後に残ります。全体を書き直す方式では、判断より書き戻しのほうが失敗しやすいという逆転が起きます。第9回で書いた「加工した先はもう原本ではない」が、検品では加工した先を納品するという形で正面から効いてきます。
4. 行数が3つとも違う
状況
同じファイルの行数を、3者が別々に報告してきました。
wc -l は 42,108,551。移行プログラムのログは 42,108,549。旧システム側の担当者は 42,108,554 と言います。差はたった5行ですが、5件のレコードがどこかで消えているのか、数え方が違うだけなのかが分かりません。
なぜ起きるか
「1行」の定義が、道具ごとに違うからです。
wc -l が数えているのは LF の個数です。だから末尾に改行がないファイルは1行少なく出ます。CRLF は LF を含むので数に入りますが、CR単独(古いMac由来、あるいはメインフレームからの変換の途中で生まれる)は数えません。一方、多くのパーサやテキストエディタは CR 単独も行区切りとして扱います。ここで数がズレます。
さらに、フィールドの中に生の CR が混ざっていることがあります。入力フォームに貼り付けられた改行がそのまま入る、というのはよくある経路です。この1文字を行区切りと解釈するかどうかで、レコードは1件から2件になります。第11回で扱った文字コードの行ズレと同じ構造で、原因は目に見えない1バイトです。
行数のズレを放置できないのは、それがそのままレコードの取り違えになるからです。5行ズレたファイルを固定長で読み込めば、ある地点から先の全レコードが1つずつずれた別人のデータになります。
汎用ツールでの対処と限界
数え方を揃えるのが定石です。
file data.txt # CRLF line terminators と出るか
grep -c $'\r' data.txt # CRを含む行の数
grep -c $'\r$' data.txt # 行末がCRLFの行の数
tr -d '\n' < data.txt | wc -c # 参考: LFを除いたバイト数
cat -A data.txt | sed -n '100,120p' # ^M と $ を可視化して確認
cat -A で可視化するところまで来れば、原因はほぼ特定できます。
限界は3つ。ひとつめ、混在の「位置」が分からないことです。 grep -c は件数を返しますが、それがファイルのどのあたりに固まっているかは返しません。移行データなら、混在は特定のバッチ・特定の時期に固まっているはずで、その分布が分かれば原因が分かります。 件数だけでは、そこへ行けません。
ふたつめ、cat -A で見られるのは、そこまで流した範囲だけであることです。 4200万行のどこかにある混在箇所へ跳ぶには、また第1章の問題に戻ります。
みっつめ、直す手段が乱暴であることです。 dos2unix や tr -d '\r' は原本を書き換えます。数十GBの全体を書き直すコストもさることながら、フィールドの中の意図的な CR まで一律に潰します。 「行区切りの CR だけを直し、値の中の CR は残す」という選択的な修正は、この一括変換ではできません。そして書き換えてしまえば、比較の基準になる原本はもうありません。
4つに共通していたもの
| 場面 | 機械が出したもの | 人が確かめたいこと | 汎用ツールが止まる場所 |
|---|---|---|---|
| パーサが落ちた行 | 行番号1つ | その行の前後に何があるか | 行番号から現物へ跳べない |
| 数億行の全量検品 | 該当行のリスト | どこに固まっているか | 見た範囲・条件が残らない |
| 重複12万件 | 一致した件数 | 消していい重複かどうか | 件数は返るが原本の座標が返らない |
| 改行コード混在 | 3つの違う行数 | どこで混ざっているか | 直そうとすると原本を書き換える |
2列目と3列目の落差が、この仕事の正体です。機械はどれも正しく答えを返しています。 行番号も、件数も、行数の差も、事実です。それでも作業が止まるのは、その数字を現物に当てる手段がないからです。検品は最終的に、人が見て判を押す仕事で、数字だけでは判が押せません。
4列目も揃っています。止まるのはいつも、「じゃあ、その行を見よう」と思った瞬間です。そして見た後には、直す段で原本を書き換えなければならないという、もう一段重い壁があります。数十GBの書き直しは時間がかかるだけでなく、失敗すると原本ごと失います。数カ月に一度の移行作業では、この失敗コストが全体の慎重さを決めています。
必要なものは3つです。
- 行番号から現物へ、待たずに跳べること — 行番号は住所ではない、という第1章の問題が解けていること。跳んだ先で前後を好きなだけ広げられること。
- 見た範囲と条件が残ること — 検品は数日にまたがります。翌日、昨日の続きから再開できること。検収資料に「どの条件で確認したか」を書けること。
- 直すときに、原本を書き換えないこと — 修正が差分として積まれ、原本がそのまま残ること。途中でやめられること。失敗しても原本に戻れること。
冒頭の「落ちたのは8342119行目」に戻ります。この検品が数日で終わるか一週間かかるかを決めているのは、エラーメッセージの精度ではありません。 その行番号を打ち込んで現物が出てくるまでの時間と、直すときに原本を賭けずに済むかどうかです。
使っている道具
私が開発している UwView(無料)は、巨大なテキストを開いた瞬間から表示・スクロール・検索できるビューアです。索引はバックグラウンドで作られ、完成すると行番号が付きます(他のビューアの多くは索引が終わるまで先頭しか見えません)。分割も抽出もしないので、原本は1本のまま・無改変のまま扱えます。
- 行番号を指定して跳ぶ: 第1章の8342119行目は、行番号を入力すればその場所が開きます。跳んだ先は原本なので、前後を好きなだけ広げて読めます。
sedのように範囲を先に決める必要がありません。 - 検索条件を保存して、翌日そこから再開する: よく使う検索は定義済みフィルタとして保存でき、セッションも復元されます。第2章の「見た範囲が残らない」問題に対して、条件そのものを成果物として残せます。
- 絞り込んだ結果から、さらに絞り込む: ドリルダウン検索は検索結果に対してさらに検索をかけます。第3章の「12万件の重複を種類別に仕分ける」ような作業を、抽出ファイルを作らずに画面の上で重ねられます。 結果の一覧からは原本の該当行へ跳べるので、原本の座標も失われません。

この画面が、第1章と第2章の答えそのものです。上のタブに Tokyo (10,967) → Ariake (7) → 有明 (4) と件数付きで段が残り、右クリックの 「履歴(絞り込みの経路)」 には 行 320,574,475: Tokyo → 行 320,574,475: Ariake と、その行がどの語で・原本の何行目で拾われたかが並びます。sort や grep のパイプを通した瞬間に消えるのが、まさにこの原本の行番号でした。8.9億行のファイルでも保持され、そこへ跳べます。検収資料に「どの条件でどこまで確認したか」を書く段で、この経路がそのまま根拠になります。
– 開き直しを軽くする: UwView Pro は索引と圧縮を保存するので、2回目以降は行番号付きのまま開き直せます(47.73GBのテキストで実測0.02〜0.07秒。特定環境での測定例で、環境により異なります)。第2章のように数日にわたって同じファイルへ入り直す作業では、この差が日数に効きます。
そして、この連載でずっと「直す段で原本を書き換えることになる」と書いてきた部分に、v1.4.0 で Edit Upgrade が入りました。
- 原本を書き換えません。 修正も一括置換も、差分ファイル(
.ewvz)に積まれます。原本はそのまま残るので、比較の基準が最後まで手元にあります。 - 修正のコストがファイルサイズに依存しません。 開発中の計測では、10GB・1億行のファイルに対する21,994箇所の一括置換が16.8秒でした(計測記事。macOS・外付けSSD・
.uwvz化済みファイルでの1回の計測で、環境により異なります)。 - 途中でやめて、翌日続きから。 全体を保存し直さずに中断・再開できます。第2章・第3章のような数日仕事のための性質です。
正直に書いておくと、UwView は検証ツールではありません。第2章の「列数が違う行を全量から抜く」は awk の仕事で、この道具の出番はその後(出てきた行番号を現物に当てる)です。CSVの列を理解しません——列名でのソートも型チェックもスキーマ推定もしないので、「巨大CSVエディタ」を期待すると外れます。集計もしません。第3章の重複件数を数えるのは sort と uniq の仕事です。そして Edit Upgrade の編集対象は .uwvz 化したファイルです。生のCSVをその場で直す使い方ではなく、いったん .uwvz にしてから扱う設計で、View License への追加ライセンスとして動きます(単体では動作しません)。できるのは、見つけること・現物を示すこと・原本を壊さずに直すことです。
数カ月に一度の移行ダンプ修正や、失敗できない数日仕事に当たっているなら、まず試してみてください。
- 14日間無料で試す(View+Edit の全機能・支払い情報の登録なし)
- UwView Pro(View) — 永続索引・圧縮キャッシュ検索・約1/9保管で、開き直しも検索も一段速くなります(全OS対応・買い切り/月額プランあり)
- Edit Upgrade — 非破壊の差分編集・巨大ファイルの一括置換・中断して翌日再開(View License への追加ライセンス)
リンク
- 第1回: 巨大ファイルに沈む4つの定番ツールと、その先: https://uvp.y42u.net/blog/uwview-ps01-huge-file-tool-limits/
- 第2回: ログの因果を遡る4つの技法: https://uvp.y42u.net/blog/uwview-ps02-log-causality-tracing/
- 第3回: 文字コード4つの罠と切り分け手順: https://uvp.y42u.net/blog/uwview-ps03-japanese-encoding-traps/
- 第4回: 消す・残す・圧縮するの判断基準: https://uvp.y42u.net/blog/uwview-ps04-log-retention-decision/
- 第5回: 開発ログとの正しい付き合い方4選: https://uvp.y42u.net/blog/uwview-ps05-debug-log-practices/
- 第6回: 認証ログ数千万行から痕跡を拾う4手順: https://uvp.y42u.net/blog/uwview-ps06-intrusion-triage-auth-logs/
- 第7回: タイムスタンプを武器にする4つの読み方: https://uvp.y42u.net/blog/uwview-ps07-timestamp-driven-triage/
- 第8回: 圧縮保管と検索の両立: https://uvp.y42u.net/blog/uwview-ps08-compressed-archive-search/
- 第9回: 構造化データを生のまま読む4場面: https://uvp.y42u.net/blog/uwview-ps09-read-raw-structured-data/
- 第10回: 深夜2時の自分を助ける4つの仕込み: https://uvp.y42u.net/blog/uwview-ps10-oncall-night-preparation/
- 第11回: レガシー文字コードを2026年に読む: https://uvp.y42u.net/blog/uwview-ps11-legacy-encoding-euc-utf16/
- 第12回: 問い合わせ対応のログ調査4パターン: https://uvp.y42u.net/blog/uwview-ps12-support-inquiry-log-tracing/
- 1億行の一括置換を計測する: https://uvp.y42u.net/blog/uep-100m-lines-replace-all/
- 絞り込んだ先からさらに絞り込む: https://uvp.y42u.net/blog/uvp-drilldown-search/
- 並び順を条件にして探す: https://uvp.y42u.net/blog/uvp-sequence-search/
- ソースコード(GitHub): https://github.com/amru195704/UwView
開発者より: アプリ・Kindle本・オープンソースの一覧は GitHub: amru195704 にまとめています。
お願い
本記事の情報は参考目的で掲載しており、正確性・完全性を保証するものではありません。掲載したエラーメッセージ・行数・件数は説明のための例であり、特定の実在するデータや案件を示すものではありません。CSVのクォート解釈・改行コードの扱いは、パーサやライブラリの実装・バージョンにより異なります。記載した実測値は特定の1環境での測定例であり、同じ結果を保証するものではありません。コマンド例は環境(GNU/BSD、awk・sort・grepの実装差、シェルの$'\r'展開の可否等)により調整が必要です。移行対象データの取り扱いは、所属組織および委託元の規程に従ってください。誤記・不正確な情報がございましたら、コメント欄よりご指摘いただければ、確認のうえ修正いたします。

