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のようにメディアタイプのパラメーターで示せます。

参照した仕様とドキュメント

リンクと内容は2026年10月3日に確認しました。改訂案の状態や各言語のドキュメントは変わることがあります。

ブログへ