2011-01-04 [長年日記]

[Haskell] ParsecでByteStringを使ってUTF-8の文字列をパースする

ParsecでByteStringを使う場合、Text.Parsec.ByteStringやText.Parsec.ByteString.Lazyを使いますが、これらのパッケージは、Data.ByteString.Char8やData.ByteString.Lazy.Char8を中で使っているので、非ASCIIな文字列を正しくパースできません。

UTF-8のバイト列がByteStringに格納されている場合には、utf8-stringパッケージのData.ByteString.UTF8やData.ByteString.Lazy.UTF8を使うとパースできるようになります。まずは、Streamとして扱えるように、以下のようにインスタンスを宣言します。

import qualified Data.ByteString.Lazy.UTF8 as U

instance Monad m => Stream U.ByteString m Char where
    uncons = return . U.uncons

これで、UTF-8の一文字ずつを読み込むストリームができたので、あとはByteStringを普通にパースします。

import qualified Data.ByteString.Lazy as B
import Text.Parsec.Prim

parseFile path = do
  input <- B.readFile path
  result <- runPT parser () path input
  case result of
    Right m -> return m
    Left e -> ioError $ userError $ show e

ただし、Text.Parsecをインポートすると、Text.Parsec.ByteString.Lazyもインポートされてしまうため、ここで定義されているChar8版のインスタンス宣言とぶつかってしまいます。回避するためには、Text.Parsec.PrimやText.Parsec.Combinatorなどなどを個別にインポートする必要があります。

もしくは、newtypeで別の型にして、そちらを使うか。

newtype UTF8ByteString = UTF8ByteString B.ByteString

instance Monad m => Stream UTF8ByteString m Char where
    uncons (UTF8ByteString s) = return $ second UTF8ByteString <$> U.uncons s

トップ «前の日記(2010-12-28) 最新 次の日記(2011-01-08)»