CSVのRFC 4180:カンマ・ダブルクォート・改行を含む値の扱い方
値の中のカンマや改行でCSVの列がずれる理由を、RFC 4180、IANAのtext/csv登録、W3Cの表形式データ勧告、PythonとGoの公式ドキュメントで整理します。RFC 4180が決めていないことも確認します。
CSVは「カンマで区切ったテキスト」なので、手で作るのも簡単に見えます。ところが値にカンマや改行が一つ入るだけで、列がずれたり行が分かれたりします。そのとき基準として挙げられるのがRFC 4180ですが、この文書が何を決め、何を決めていないかは意外と知られていません。
RFC 4180はインターネット標準ではない
RFC 4180は2005年10月に出たInformational RFCで、冒頭に「いかなる種類のインターネット標準も規定しない」と書かれています。2章では正式な仕様が存在しないと述べたうえで、「ほとんどの実装が従っているとみられる形式」を記録しています。このRFCが正式に行ったのは、text/csvメディアタイプのIANAへの登録です。
2014年にはRFC 7111が、CSVの行・列・セルを指すURIフラグメント識別子を定義してRFC 4180を更新しました。RFC 4180を置き換えるための改訂案draft-shafranovich-rfc4180-bisは、IETF Datatracker上で第07版を最後に失効しています(2026年10月3日確認)。
クォートの規則:囲み、中の引用符を二重にする
RFC 4180の2章にある主な規則は次のとおりです。
- 改行・ダブルクォート・カンマを含むフィールドはダブルクォートで囲む。
- 囲んだフィールド内のダブルクォートは、もう一つ重ねて
""と書く。 - 囲んでいないフィールドにはダブルクォートを含められない。
- 空白もフィールドの一部なので無視しない。最後のフィールドの後ろにカンマを付けない。
id,memo
1,"He said ""hi"", then left
line2"
見た目は3行ですが、ヘッダーを除くとレコードは一つです。2番目のフィールドがダブルクォートで囲まれているので、中のカンマと改行は値の一部として扱われます。
改行で分割してはいけない理由
RFC 4180はレコードの区切りをCRLFとし、最後のレコードの後の改行はあってもなくてもよいとしています。文法を定めたABNFでは、囲んだフィールドの中にCRとLFを含められます。そのため、ファイルを改行でsplitすると、クォート内の改行でレコードが切れてしまいます。CSVを読むには、現在位置がクォートの内側かどうかを追うパーサーが必要です。
改行の扱いは実装によっても違います。
- Pythonの
csvは、書き込み時の既定の行末が\r\nです。読み込みではlineterminatorの設定に関係なく\rと\nの両方を行末として認識します。公式ドキュメントは、ファイルをnewline=''で開くよう案内しています。指定しないと、クォート内の改行が正しく解釈されなかったり、\r\nを使う環境で\rが余分に付いたりすることがあります。 - Goの
encoding/csvはRFC 4180の形式に対応していますが、Writerは既定でCRLFではなくLFを使います。UseCRLFで変更できます。Readerは複数行フィールドの中も含め、\r\nをすべて\nに変換します。 - W3Cの2015年の勧告「Model for Tabular Data and Metadata on the Web」は、行末にCRLFを推奨しながらLFも認めています。この点がRFC 4180の
text/csvの定義と異なることは、文書自身が明記しています。
Python 3.14で次のコードを実行すると、レコードの終わりは\r\n、フィールド内の改行は\nになります。ABNFではクォート内にCRとLFを含められるため、両方が一つのファイルに現れることがあります。
import csv, io
buf = io.StringIO(newline="")
w = csv.writer(buf)
w.writerow(["id", "memo"])
w.writerow([1, 'He said "hi", then left\nline2'])
print(repr(buf.getvalue()))
# 'id,memo\r\n1,"He said ""hi"", then left\nline2"\r\n'
文字集合:文法はASCII、登録ではUTF-8とみなすことを推奨
RFC 4180のABNFでは、フィールドの内容であるTEXTDATAは%x20-21 / %x23-2B / %x2D-7Eです。カンマとダブルクォートを除く印字可能なASCII文字だけなので、日本語はこの文法の範囲外です。RFCは、一般にはUS-ASCIIが使われるものの、charsetパラメーターで他の文字集合も指定できると説明しています。W3C勧告も、このASCII制限は実際には日常的に無視されており、国際的なウェブでは現実的でないと指摘しています。
現在のIANAのtext/csv登録は2014年1月17日に更新されたものです。この登録はcharsetパラメーターの使用を推奨し、指定がない場合はUTF-8とみなすことを推奨しています。W3C勧告もUTF-8を推奨しています。
ファイル先頭のBOMがプログラムによって違う扱いを受ける問題は、UTF-8 BOMの記事で扱いました。Shift_JISで保存されたCSVは、文字コード変換ツールでUTF-8に変換できます。
まとめ
- RFC 4180はインターネット標準化トラックの仕様ではなく、情報提供を目的とした文書です。ABNF文法は示していますが、本文の小文字の「should」と「must」は、RFC 2119の大文字の要求レベルキーワードではなく通常の文章です。
- CSVを書くときは、カンマ・ダブルクォート・改行を含むフィールドを囲み、中のダブルクォートを二重にします。文字列を自分でつなぐより、言語のCSVライブラリを使うほうが安全です。
- CSVを読むときは改行で分割しません。レコードの行末がCRLFかLFかは、ファイルを作ったプログラムによって異なることがあります。
- 文字コードやヘッダー行の有無は、ファイルだけでは分からないことがあります。HTTPで配信するなら、
text/csv; charset=utf-8; header=presentのようにメディアタイプのパラメーターで示せます。
参照した仕様とドキュメント
- RFC 4180 — Common Format and MIME Type for Comma-Separated Values (CSV) Files:2章(形式とABNF)、3章(
text/csvの登録) - RFC 7111 — URI Fragment Identifiers for the text/csv Media Type
- IANA:text/csvメディアタイプの登録
- IETF Datatracker:draft-shafranovich-rfc4180-bis
- W3C:Model for Tabular Data and Metadata on the Web:7.2・7.3節、付録B.1
- Python標準ライブラリ:csv
- Go標準ライブラリ:encoding/csv
リンクと内容は2026年10月3日に確認しました。改訂案の状態や各言語のドキュメントは変わることがあります。