はじめに
こんにちは、42tokyo Advent Calendar 2023の3日目を担当する、42tokyoの在校生、hnoguchiです。
現在取り組んでいる課題で、csvファイルを読み込む実装を行なっており、RFCでの定義を整理するために書き始めました。
この記事は、RFC4180の中でも、2. Definition of the CSV Format をまとめたものになります。
間違いがあったらご指摘ください。どうぞよろしくお願いします。
まとめ
ABNF
file = [header CRLF] record *(CRLF record) [CRLF]
header = name *(COMMA name)
record = field *(COMMA field)
name = field
field = (escaped / non-escaped)
escaped = DQUOTE *(TEXTDATA / COMMA / CR / LF / 2DQUOTE) DQUOTE
non-escaped = *TEXTDATA
COMMA = %x2C
CR = %x0D
DQUOTE = %x22
LF = %x0A
CRLF = CR LF
TEXTDATA = %x20-21 / %x23-2B / %x2D-7E
2. CSVファイルの書き方について(定義)
そもそも、CSVの書き方には様々な仕様や実装が存在する。
そして、多様な書き方の解釈を可能にする正式な仕様は存在しない。
ここでは、多様な書き方の中で共通していると思われる書式を文書化した。
各recordは、改行(CRLF)で区切られる。
sample.csv
field_1,field_2,field_3[CRLF]
field_1,field_2,field_3[CRLF]
...
最終recordは、改行(CRLF)がなくとも良い。 (may or may not)
sample.csv
...
field_1,field_2,field_3[CRLF]
field_1,field_2,field_3
先頭行が、headerの可能性がある。
sample.csv
field_1_name,field_2_name,field_3_name[CRLF]
field_1,field_2,field_3[CRLF]
...
- header行のfield数と各recordのfield数は、同数であると良い。(should be)
- header行の有無は、MIMEタイプのoption "header" parameterで指定しておくと良い。(should be)
headerと各recordの中には、comma(,)で区切られたfieldが1つ以上あって良い。
sample.csv
// ダメな書き方
field_1,field_2,field_3,[CRLF]
...
- 各行のfield数は、同数であると良い。(should)
- [space]はfieldの一部とみなす。無視してはならない。(should not be)
- recordの最終fieldの末尾には、comma(,)を付けてはいけない。(must not)
各fieldは二重引用符(")で囲んでも良い。(may or may not)
- 二重引用符(")に全く対応していない実装もある。
- 二重引用符(")で囲まれていないfieldに二重引用符(")がある場合、その二重引用符(")を無視するものがある。(may not)
改行[CRLF]、二重引用符(")、comma(,)を含むfieldは二重引用符(")で囲むべき。(should be)
sample.csv
"field_1","field,_2","field[CRLF]
_3"[CRLF]
field_1,field_2,field_3[CRLF]
...
二重引用符(")を文字として扱うには、fieldを二重引用符(")で囲み、二重引用符(")でescapeする。(must be)
sample.csv
"field""_1","field""""_2","field_3"[CRLF]
...
さいごに
明確な定義(must)が少なく、あくまで推奨する定義(should)が多い印象でした。
二重引用符(")で囲まれたfieldでは、改行(CR, LF)も受け入れるところが面白かったです。
次回は、これらの定義を参考にC++98
で、parserの実装をしていきたいと思います。(未定)
ありがとうございました。
参考
RFC 4180 - Common Format and MIME Type for Comma-Separated Values (CSV) Files