F# でパーサーコンビネータを使った CSV ファイルのパース処理 - FParsec 使用

前回(id:fits:20101226)実施したパーサーコンビネータによる CSV ファイルのパース処理を FParsec を使って F# でやってみました。

環境は以下の通り。

サンプルのソースは http://github.com/fits/try_samples/tree/master/blog/20101231/

事前準備 - FParsec のビルド

https://bitbucket.org/fparsec/main/downloads/ から FParsec のソースコードをダウンロードした後、適当なディレクトリに解凍しビルドを行います。

ビルド
> cd fparsec\Build\VS10
> msbuild FParsec.fsproj

bin\Debug ディレクトリに作成された FParsec.dll と FParsecCS.dll ファイルをサンプルを作成するディレクトリにコピーしておきます。

CSVファイルのパース

Haskell で書いたサンプルとほぼ同じ実装でよかったので、いきなり以下の CSV ファイルをパースしてみる事にします。

test.csv
1,テスト1,"改行
含み"
2,test2,"カンマ,含み"
3,てすと3,"ダブルクォーテーション""含み"

Haskell の Parsec との違いは以下のような点です。

  • try の代わりに attempt を使う
  • char の代わりに pchar を使う
  • string の代わりに pstring を使う
  • return の代わりに preturn を使う
  • many の代わりに manyChars を使う
  • endBy の代わりに sepEndBy を使う

また、Scala の ~> 等の代わりに以下が使えました。

  • ~> の代わりに >>. を使う
  • <~ の代わりに .>> を使う

ちなみに、newline は FParsec.CharParsers で定義されているので、自前で定義する必要はありません。

parse_csv.fs
open System
open FParsec.Primitives
open FParsec.CharParsers

let quotedChar = noneOf "\"" <|> attempt (pstring "\"\"" >>. preturn '"')
let quotedCell = pchar '"' >>. manyChars quotedChar .>> pchar '"'
let cell = quotedCell <|> manyChars (noneOf ",\n")
let line = sepBy cell (pchar ',')
let csvFile = sepEndBy line newline

let cs = Console.In.ReadToEnd()
let res = run csvFile cs

match res with
| Success (v, _, _) -> Console.WriteLine(v)
| Failure (msg, _, _) -> Console.WriteLine(msg)
ビルド・実行結果
> fsc /r:FParsec.dll parse_csv.fs
> parse_csv.exe < test.csv
[[1; テスト1; 改行
含み]; [2; test2; カンマ,含み]; [3; てすと3; ダブルクォーテーション"含み]; ... ]