名前と点数の CSV を読んで、合計を出す。練習によくある問題です。1行目の見出しが「名前,点数」かを確かめてから読み進める、10言語のよくある書き方を用意しました。
ところが、Excel で「CSV UTF-8」として保存した形のファイルを渡すと、10言語中10言語が「見出しの行がありません」と答えました。中身はふつうの CSV と同じです。違いは、ファイルの先頭に付く目に見えない印、BOM(U+FEFF)だけです。
何をしたか
CODE Polyglot の入門 第12回 は、標準入力から CSV を読んで、1人ずつの点数と合計を出すプログラムです。正解表では、改行は LF でも CRLF でもよく、先頭の BOM は無視することにしてあります。
よくある書き方は、行に分けて、1行目を見出しと比べるものです。Python ならこうです。
lines = sys.stdin.read().split("\n")
if lines[0] != "名前,点数":
print("見出しの行がありません")
sys.exit()
毎朝の検証と同じ Linux の環境で動かした結果です(9件の正解表から、Excel に関係する2件と、カンマ・" を含む2件)。
| 入力 | Python・JavaScript・PHP・Ruby | Go・Java・Kotlin・C#・Rust・Swift |
|---|---|---|
| 先頭に BOM(Excel の「CSV UTF-8」) | 見出しの行がありません | 見出しの行がありません |
| 改行が CRLF(Windows・Excel) | 見出しの行がありません | 正しく合計した |
名前にカンマ("山田, 太郎",80) | 間違えた | 間違えた |
名前に "("自称""先生""",90) | 間違えた | 間違えた |
BOM は、どの言語も取らなかった
10言語とも、行を読む機能は先頭の BOM を取り除きませんでした。1行目は「[BOM]名前,点数」になり、見た目は同じでも「名前,点数」とは一致しません。エラーにはならず、ただ比較が合わないだけなので、画面に出した見出しを目で見ても気づけません。
CRLF は、読み方で分かれた
Windows や Excel で作った CSV は、改行が CRLF(\r\n)のことがあります。
- 落ちた(10言語中4言語): Python・JavaScript・PHP・Ruby。全部を読んでから
\nで区切ったので、行の最後に\rが残り、見出しが「名前,点数\r」になった - 通った(10言語中6言語): Go の
bufio.Scanner・Java のreadLine・Kotlin のreadLine・C# のReadLine・Rust のlines()・Swift のreadLine。1行ずつ読む機能が\rも取り除いた
「,」で区切るだけでは読めない行
名前にカンマが入ると、CSV では "山田, 太郎",80 のように " で囲みます。これを「,」で区切ると "山田 と 太郎" と 80 に分かれ、点数の位置に 太郎" が来ます。
- エラーで止まった(10言語中6言語): Python・Java・Kotlin・C#・Rust・Swift
- 黙って間違えた(10言語中4言語): Go・PHP・Ruby は
"山田: 0と出して合計を 72 に、JavaScript は"山田: NaNと出した
止まる言語は気づけますが、黙って間違える言語は、合計がずれたまま次の処理に進みます。"自称""先生"""(" を "" と2つ重ねる書き方)は、10言語とも " が残ったまま出ました。
どう直したか
BOM を取り除き、CSV を読む標準の機能があればそれを使います。Python なら、文字コードを utf-8-sig にすると BOM が取れ、csv が "…" と CRLF を読み分けます。
# utf-8-sig は先頭の BOM を取り除く。newline="" で改行の扱いを csv に任せる(CRLF も "…" の中の改行も読める)
sys.stdin.reconfigure(encoding="utf-8-sig", newline="")
# csv は "…" で囲んだ項目や "" を読み分ける。空の行は [] になるので飛ばす
rows = [row for row in csv.reader(sys.stdin) if row]
CSV を読む標準の機能が無い JavaScript では、BOM を自分で取り、1文字ずつ見て「今 "…" の中にいるか」を覚えながら区切りました。
// 先頭の BOM(U+FEFF)を取り除く。空の行は [""] になるので飛ばす
const text = require("fs").readFileSync(0, "utf8").replace(/^/, "");
直した版は、10言語とも9件中9件が正解になりました。
まとめ
- Excel の「CSV UTF-8」の先頭の BOM は、10言語とも行を読む機能が取らなかった。見出しの比較は黙って失敗する
- CRLF は、全部読んで
\nで区切ると\rが残る(10言語中4言語)。1行ずつ読む機能は取ってくれた - 「,」で区切るだけでは、カンマや
"を含む名前を読めない。止まる言語と、黙って間違える言語がある - CSV を受け付けるなら、Excel で保存したファイルをテストに入れる
- 10言語の全プログラムは 入門 第12回 にあります
- 第1回:「2026-02-30」を渡したら、PHP と JavaScript だけが黙って3月2日にした
- 第2回:JavaScript で「月の最終日」を出したら、日本では毎回1日前になった
- 第3回:C# の Normalize(FormKC) が、Docker の中では何もしなかった
- 第4回:辞書の「取り出す順番」を10言語で20回ずつ試したら、5通りの性格に分かれた
- 第5回:「+5」を整数に直したら、10言語すべてが黙って 5 にした
- 第6回:メールアドレスの正規表現の「$」が、最後の改行を通した
- 第7回:「東京,大阪,」を split したら、Java・Ruby・Swift は最後の空の項目を捨てた
- 第8回:Excel の「CSV UTF-8」を読ませたら、10言語すべてが見出しの行を見失った(この記事)
Analyzegear


