📌

あなたのCSVが会社を潰す日 〜非エンジニアが知らずにやらかすWindowsの罠〜

に公開

CSV×改行コード(CRLF/LF)で二度と死なない:Excel/Windows産CSVをLinuxサーバへ安全に流し込む実戦手引き

結論

Windowsで作ったCSVをそのまま渡すと、相手(サーバやDB、エンジニアのスクリプト)側で行の区切りがズレて「カラム数が合わない」「文字化け」「取り込み失敗」などが起こります。対策は (1) 改行のそろえ(EOL統一)(2) "CSVとして"正しく読み込むこと の二本柱。この記事の手順をコピペすれば回避できます。


これは"非エンジニアのあなた"に起きがちな事故です

  • 「私がExcelで作ったデータで夜中にシステムが止まった」
  • 「売上レポートが文字化けして、役員会議が延期になった」
  • 「顧客リストの取り込みが失敗して、メール配信が止まった」

実はこれ、あなたの作業が悪いわけではありませんWindowsの改行のしかたCSVのルールの"ちょっとしたズレ"が原因の環境差のせいです。でも、知らないとヤバいことになります。


実録:非エンジニアが引き起こした大事故(5選)

1. 「売上データが消えた」月末の地獄絵図

登場人物:営業企画のAさん(Excel大好き、IT知識ゼロ)

状況:毎月末、全国の売上データをExcelで整理してシステムに一括アップロード。今月は過去最高売上を記録し、役員も大注目。

事件発生:いつも通りCSVで書き出してアップロード→「処理中...」のまま3時間固まる→全データが中途半端に取り込まれて売上が半分以下に表示

大混乱

  • 役員「売上半減?!何があった?!」
  • 営業部長「システム障害ですか?」
  • 情シス緊急招集→夜中の3時まで復旧作業
  • 翌朝の役員会議は「データ信頼性の問題」で大炎上

真犯人:Aさんが見やすさのために住所欄で**Alt+Enter(セル内改行)**を多用→CSV出力時に引用符が一部外れて行がバラバラに→取り込み処理が暴走

Aさんの心境:「私、何か悪いことした...?普通にExcelで整理しただけなのに...会社に迷惑かけちゃった...」


2. 「顧客に間違った人の個人情報を送信」プライバシー大事故

登場人物:マーケティング部のBさん(キャンペーン担当、几帳面)

状況:新商品キャンペーンで1万人にパーソナライズメールを送信予定。顧客データベースからExportしたCSVに、手作業で「特別オファー金額」を追加して一括処理。

事件発生:配信開始30分後、お客様相談室に電話が殺到

悲劇の内容

  • 田中さんに「佐藤様、あなたの年収1200万円に相応しい...」
  • 佐藤さんに「山田様、シングルマザーのあなたを応援...」
  • 山田さんに「鈴木様、ご主人の定年後の資産形成...」

原因:顧客名の一部に**会社名(改行付き)**が含まれていた→CSV処理でデータがズレて個人情報が完全に入れ替わり

その後

  • 謝罪対応に1週間、法務部も巻き込んで大騒動
  • 個人情報保護委員会への報告書作成
  • Bさん、責任を感じて体調不良で1ヶ月休職

Bさんの証言:「まさか自分がデータを作っただけで、お客様にご迷惑をかけるなんて...もうExcelが怖くて触れない」


3. 「給与計算が狂って全社員に間違った給与を支給」

登場人物:人事部のCさん(勤続15年のベテラン)

状況:働き方改革で在宅勤務手当や時差出勤手当が新設→給与システムが対応していないため、手当部分だけExcelで管理してCSVで給与システムに追加取り込み

大惨事:給与支給日の朝、全社員500人の銀行口座に給与振り込み完了→新入社員が部長より高給取りに、部長の給与が最低賃金以下に

阿鼻叫喚

  • 新入社員D君「えっ、僕の給料80万円?間違いですよね?」
  • 部長「俺の給料8万円って...バイト以下じゃないか!」
  • 経理「銀行への振り込み取り消しは不可能です」
  • 社長「信用問題になる、すぐ何とかしろ!」

原因:手当CSV内のコメント欄に改行を含む「業務内容詳細」を記載→取り込み時にデータがずれて、社員番号と金額がバラバラに対応

後処理

  • 全員に謝罪と訂正振り込み(2日後)
  • 労基署から「賃金支払いの不備」として指導
  • Cさん「15年間ミスしたことなかったのに...」

4. 「株主総会資料の数字が全部デタラメ」

登場人物:IR担当のEさん(数字に強い、責任感強すぎ)

状況:上場企業の四半期決算発表前日、最終チェックのため会計データをExcelで整理→投資家向け資料に使う重要指標をCSV出力

絶望的展開:翌日の決算説明会で、売上成長率が-95%、営業利益率が400% などの異常数値を堂々と発表

会場の様子

  • 投資家A「売上がほぼゼロ?倒産するんですか?」
  • 投資家B「利益率400%って...粉飾決算ですか?」
  • アナリスト「こんなデタラメな数字、初めて見た」
  • 株価、決算発表直後に30%暴落

真相:財務データの「前年同期比」列に改行付きコメント(「※為替影響考慮\n実質成長率は...」)が混入→CSV処理で数値列がコメント列とずれて、とんでもない計算結果に

Eさんのその後:「会社の株価を暴落させた女」として社内で有名に→転職を決意


5. 💀「薬事法違反で業務停止命令」製薬会社の悪夢

登場人物:品質管理部のFさん(薬学修士、超真面目)

状況:新薬の臨床試験データをExcelで管理→承認申請用にCSVで厚労省システムへ提出

致命的ミス:データ取り込み時に患者の症状記録が別の患者と入れ替わり→「重篤な副作用なし」の患者が「重篤な副作用あり」として申請

発覚と大惨事

  • 承認審査中に厚労省が異常を発見
  • 「臨床データの信頼性に重大な疑義」として審査中止
  • 業界紙一面「○○製薬、データ改ざん疑惑」
  • 株価50%下落、業務停止命令3ヶ月
  • 新薬開発費100億円が水の泡

Fさんの告白:「患者さんの命に関わるデータなのに...私の手でこんなことになるなんて。一生償いきれない」

原因:症状記録に改行付きの長文があったが、CSV出力時の引用処理が不完全→患者IDと症状データの対応が崩壊


あなたも明日やらかすかもしれない理由

これらの事故、**全て「普通にExcelを使っただけ」**で起きています。

  • Windowsで普通にExcelを使う
  • 見やすくするためにセル内で改行(Alt+Enter)
  • CSV保存してシステムに渡す

この当たり前の流れで大事故が起きるのが現実です。

しかも**「昨日まで正常だったのに今日だけ異常」**というパターンが多く、原因究明まで時間がかかって被害が拡大します。

「私は単純なデータしか扱わないから大丈夫」 ←これが一番危険。気づかないうちに改行やコメントが紛れ込んで、いつか必ず事故ります。


なにが起きているの?(やさしい説明)

  • 改行の記号がOSで違う

    • Windowsは行の終わりに CRLF(2文字)
    • macOS/Linuxは LF(1文字)
  • CSVはルールがある

    • セルの中に改行があってもOK(ただし**"で囲む**必要あり)
    • "(ダブルクォート)自体が入るときは "" と二重に

エンジニア側の取り込み(サーバ/DB/スクリプト)がこれらを**“文字通りCSVとして”扱っていない**と、行が途中で切れたり、見えない文字(
)が混ざって数値にできなかったりします。

macなら大丈夫? いいえ。Excel for Macが出すCSVでも、Windows式の改行やBOMが入ることがあり、同じ事故が起きます。


あなたができる安全対策(コピペでOK)

  • 渡す前に“改行をそろえる”(CRLF→LF)
  • 取り込み側は“CSVとして読む”(行分割ではなく、CSVモード/専用パーサを使ってもらう)

この記事には、

  • 前処理のコピペ用コマンド(Windows/ Mac /Linux)
  • DB取り込みの正しい設定例(PostgreSQL / MySQL)
  • 壊れやすいパターンのチェック方法
    をまとめました。仕組みはあと回しでも、手順だけ真似すれば事故は止まります。

想定読者

  • Windowsで出力したCSVを Linux/macOSサーバ のDBに取り込む人
  • Excel/業務システムが吐くCSVで取り込みが不安定な現場
  • CSVパースを自作していて“行数/カラム数が合わない”系の不具合に悩む人

この記事のゴール

  • 改行コードとCSV仕様の要点を 実務目線 で整理
  • 再発防止テンプレ(前処理ワンライナー/DB取り込みレシピ/CIチェック)を配布

まず用語整理:EOL(改行コード)の違い

OS 記号 実体
Windows CRLF \r\n
macOS / Linux / Unix LF \n
  • エディタや簡易スクリプトが \n だけでsplit すると、末尾に \r が残って "\rOK" のようなゴミ文字化→数値変換エラー、JOINミスなどに波及。
  • ExcelのCSVプラットフォームを問わずCRLF を出すケースが多い(Excel for Macでも)。

CSVの“正解”挙動(RFC 4180 要旨)

  • 改行やカンマを含むフィールドは 二重引用符 " で囲む
  • フィールド内の二重引用符は "" と二重化 してエスケープ
  • 行の区切りは原則レコード境界(=EOL)。引用符で囲まれたフィールド内のEOLは1セル内の文字列として扱われる

つまり、フィールド内改行はOK。ダメなのは 引用なしで生改行が入っているCSVEOLと引用の混在・不整合


よくある症状

  • 取り込みツールで カラム数が合わない(途中で行が切れてしまう)
  • 数値列やフラグ列に \r が混入 して cast エラー
  • 先頭カラム名に BOM(\uFEFF) が混入→カラム名不一致

まずは現物診断(安全な読み方)

# CR(\r) を含む行を検出(^M表示)
grep -n $'\r' file.csv | head

# 引用符の不整合(簡易チェック:行ごとの '"' 出現数が奇数なら怪しい)
awk -F '"' '{if ((NF-1)%2==1) print "unbalanced at line", NR}' file.csv | head

# 文字コード/BOMの確認
file -I file.csv     # application/csv; charset=utf-8 など
xxd -g 1 -l 3 file.csv  # 先頭が EF BB BF なら UTF-8 BOM

前処理テンプレ:EOL正規化(LF統一)

Linux/WSL/macOS 共通

# 破壊的にCR削除(CRLF→LF)
sed -i '' -e 's/\r$//' file.csv  # macOS(BSD sed)
# Linuxなら: sed -i 's/\r$//' file.csv

# 非破壊で出力を分けたい場合
tr -d '\r' < file.csv > file_lf.csv

# 専用ツール(Homebrewで入る)
dos2unix file.csv

Windows PowerShell

(Get-Content file.csv -Raw) -replace "`r`n","`n" |
  Set-Content file.csv -NoNewline -Encoding utf8

ポイント: 中身をCSVとして壊さない。EOL正規化はOKだが、カンマや引用の置換は厳禁


DB取り込みの“正しい姿”

PostgreSQL

COPY mytable(col1, col2, ...)
FROM '/path/file.csv'
WITH (
  FORMAT csv,
  HEADER true,
  DELIMITER ',',
  QUOTE '"',
  ESCAPE '"'
);
  • FORMAT csv を使う限り、引用付きフィールド内改行も扱える。CRLF/LFはどちらも処理可。

MySQL / MariaDB

LOAD DATA INFILE '/path/file.csv'
INTO TABLE mytable
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES;
  • Windows由来なら LINES TERMINATED BY '\r\n' を明示。LFに正規化した場合は \n を指定。

SQLite(補足)

.mode csv
.import file.csv mytable
  • 混在EOLは不安定。事前にLFへ正規化が無難。

アプリ側の“壊れない読み方”

Python(標準csv)

import csv
with open('file.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)  # 引用・改行を仕様通りに処理
    for row in reader:
        ...

pandas

import pandas as pd
df = pd.read_csv('file.csv', dtype=str, keep_default_na=False)

Node.js(例:csv-parse)

import { parse } from 'csv-parse'
import fs from 'node:fs'

fs.createReadStream('file.csv')
  .pipe(parse({
    columns: true,
    relax_quotes: false,
    relax_column_count: false
  }))
  .on('data', r => { /* ... */ })

Go

r := csv.NewReader(file)
r.LazyQuotes = false // 仕様に厳格
records, err := r.ReadAll()

重要:readlines() で行を split しない。必ず CSV パーサに任せる。


エディタ & Git の地雷回避

VS Code

  • 右下の End of LineLF に変更
  • 設定例:
{
  "files.eol": "\n",
  "editor.renderControlCharacters": true
}

Git

git config --global core.autocrlf input   # macOS/Linux
# WindowsでLF維持したい場合も 'input' を推奨

.gitattributes でCSVだけLFを強制:

*.csv text eol=lf

BOM/文字コードの取り扱い

  • 先頭に UTF-8 BOM があると、ヘッダ名に不可視文字が混入 → カラム不一致
  • 事前に除去:
# 先頭3バイトのBOMを剥がす(非破壊)
tail -c +4 file.csv > file_nobom.csv
  • もしくは iconv で再エンコード:
iconv -f utf-8 -t utf-8 file.csv -o file_nobom.csv

実戦レシピ:ワンショット取り込み

set -euo pipefail
in="file.csv"
out="/tmp/file.lf.csv"

# 1) EOLをLFに正規化
tr -d '\r' < "$in" > "$out"

# 2) 構造チェック
csvclean -n "$out" || { echo "CSV structure looks suspicious"; exit 1; }

# 3) PostgreSQLへ(例)
psql -v ON_ERROR_STOP=1 -c "\
  COPY mytable(col1,col2,...) FROM '$out' \
  WITH (FORMAT csv, HEADER true, DELIMITER ',', QUOTE '"', ESCAPE '"');"

CIでの再発防止(プリチェック)

pre-commitフック例

#!/usr/bin/env bash
set -euo pipefail
bad=$(grep -IlR $'\r' -- '*.csv' || true)
if [ -n "$bad" ]; then
  echo "[EOL] CRLF detected in:"; echo "$bad"; exit 1
fi
# 引用不整合の簡易検査
echo "$@" | xargs -r -n1 awk -F '"' '{if ((NF-1)%2==1) {print FILENAME ": line" NR " has unbalanced quotes"; exit 1}}'

.gitattributes も併用:

*.csv text eol=lf

FAQ

Q. macOSはUNIX系だから問題にならない?
A. No。特定条件下でだめ。macOS自体はLFだが、たとえばExcel for MacWindows生成のCSV を扱うと CRLFやBOM混入 で同様に破綻する。取り込み先がLinuxでもmacOSでも、前処理と正しいローダ が必要。

Q. フィールド内改行は全部排除すべき?
A. 仕様的にはOK。どうしても運用上NGなら、書き出し側で改行→\n 等に変換し、読み込み時に復元するポリシーを決める(要合意)。

Q. 自作パーサを直せばよい?
A. 原則おすすめしない。パーサは既存実装(DBのCSVモード、言語標準、実績ライブラリ)を使う。


2. 「ヘッダが見つからない」BOMの罠

状況:マーケがExcelで出したCSVをpandasで読み、PostgreSQLへCOPY。なぜか列名idが見つからない。

症状KeyError: 'id' / DB側では column "id" does not exist

原因:CSV先頭にUTF-8 BOM() が付与。ヘッダが実際は id,name,... になっており一致せず。

再現の断片

EF BB BF 69 64 2C 6E 61 6D 65 ...  # バイト列先頭がEF BB BF

対処

  • 事前に tail -c +4 でBOM除去、または iconv 再エンコード
  • pandas側は encoding='utf-8-sig' で読み込み、DB投入前に明示的にカラム名を正規化

学びBOMは“見えない敵”。ヘッダ不一致やカラム名の取り違えはBOMを疑う。


3. 「OK

」で判定バグ—CRの居残り
状況:社内ツール(Python)が 区切りで行をsplitしてCSVを読んでいた。Windows発のCSVに切り替えた日から判定が不安定に。

症状status == 'OK' のはずがFalse判定、数値カラムのint('100 ')で例外。

原因:WindowsのCRLF行末を だけで分割したため、末尾の が値に残留。

再現の断片

for line in open('w.csv').read().split('
'):
    cols = line.split(',')
    status = cols[-1]
    # 'OK
' になっていて比較に失敗

対処

  • 入力前に CR除去(CRLF→LF)
  • そもそも csvモジュールDBのCSVモード を使う(行splitをやめる)

学び「行で割る」は事故の元。CSVはCSVとして読む。EOLは最初に正規化。


まとめ(チェックリスト)

  • EOL統一(CRLF→LF)
  • UTF-8/BOM除去
  • CSVローダ/パーサ利用(行split禁止)
  • 引用とエスケープ"..." & ""
  • DB側オプション適合FORMAT csv/LINES TERMINATED BY
  • CI/フックで検知

付録:最小再現データ

id,name,comment
1,"Alice","Hello\nWorld"
2,Bob,"He said ""Yes"""
3,Carol,
  • これを CRLF にして、素朴な split('\n') 実装 に食わせると破綻する。DBのCSVモードや言語のCSVパーサなら正しく解釈できる。

ライセンス

  • 本記事のコード片は MIT とします。業務ドキュメントやPlaybookへの転用可。

Discussion