この章では,
コンビネータとは何か?
この章でいうコンビネータとは,
関数型では,
CSVのパーサ
たとえ簡潔でも,
CSVのBNF
CSVのBNF
リスト1 CSVのBNF
csv = 1*(record CRLF) (1)
record = field *(COMMA field) (2)
field = (escaped / non-escaped) (3)
escaped = DQUOTE *(TEXTDATA / COMMA / CR / LF /
2DQUOTE) DQUOTE (4)
non-escaped = *TEXTDATA
TEXTDATA = %x20-21 / %x23-2B / %x2D-7E
COMMA = %x2C
CRLF = CR LF
CR = %x0D
LF = %x0A
DQUOTE = %x22
*
,1*
,2
はそれぞれ,
- 注1)
- Backus-Naur Form。文法を定める言語のことです。
- 注2)
- RFCで用いられるBNFは,
ABNFというBNFの亜種です。
csv, record, fieldの意味
これを見ると,
boo,foo,woo
goo,zoo,qoo
たとえば,boo,foo,woo
がrecord,foo
がfieldに当たります。これはみなさんの理解通りでしょう。
fieldの内部
難しいのはfieldです。データ中にカンマや二重符号が出てこない場合はそのままでよいのですが,
boo,"foo,woo",goo
一方,
boo,"foo""woo",goo
この例の"foo""woo"
は,foo"woo
を表します。
正規表現でCSVパーサを実装する場合
みなさんは,
正規表現がパーサを作るための技術としてあまり適していない理由は2つあります。まず第一に,
関数型言語でも正規表現のライブラリは提供されています。しかし簡単な問題であればリスト操作で解決できますし,
- 注3)
- Jeffrey E.
F. Friedl著, 株式会社ロングテール/ 長尾高弘訳, オライリー・ ジャパン, 2008年
パーサコンビネータParsecでCSVパーサを実装
では,
これから示すコードは,
import Control.Applicative ((<*),(*>))
import Text.Parsec
import Text.Parsec.String
今から,
- 注4)
- WEB+DB PRESS Vol.
67サポートサイトでも完成形を公開しています。
csvを実装する
まず,
csv :: Parser [[String]]
csv = endBy1 record crlf
csvの型を見てください。Parserとは,
内側の型は,
recordとcrlfはこれから実装するパーサです。
recordを実装する
次はリスト1
record :: Parser [String]
record = sepBy1 field comma
- 注5)
- 1回の場合は区切り文字パーサは使われません。
fieldを実装する
次はfieldです。BNFはリスト1/
の部分を,<|>
に単純に置き換えるだけです。
field :: Parser String
field = escaped <|> nonEscaped