4

私は現在、RealWorldHaskellで提示されているフルCSVパーサーを使用しようとしていますByteStringの代わりに使用するようにコードを変更しようとしましStringたが、でstring動作するコンビネータがありStringます。

前後に変換を行うことなく、stringで動作するのと同様のParsecコンビネータはありますか?ByteString

を処理する代替パーサーがあることを確認しましたがByteStringattoparsec使用方法を学習しているだけなので、Parsecを使い続けたいと思います。

4

2 に答える 2

5

私はあなたが次のようなものから始めていると思います

import Prelude hiding (getContents, putStrLn)
import Data.ByteString
import Text.Parsec.ByteString

これが私がこれまでに得たものです。2つのバージョンがあります。両方ともコンパイルします。おそらくどちらもあなたが望むものではありませんが、それらは議論を助け、あなたの質問を明確にするのに役立つはずです。

途中で気付いたことがあります。

  • import Text.Parsec.ByteString次に、これはunconsfrom Data.ByteString.Char8を使用し、次にfrom w2cData.ByteString.Internalを使用して、すべての読み取りバイトをCharsに変換します。これにより、Parsecの行番号と列番号のエラー報告が適切に機能し、string問題なく友達と一緒に使用できるようになります。

したがって、CSVパーサーの簡単なバージョンは、まさにそれを実行します。

import Prelude hiding (getContents, putStrLn)
import Data.ByteString (ByteString)

import qualified Prelude (getContents, putStrLn)
import qualified Data.ByteString as ByteString (getContents)

import Text.Parsec
import Text.Parsec.ByteString

csvFile :: Parser [[String]]
csvFile = endBy line eol
line :: Parser [String]
line = sepBy cell (char ',')
cell :: Parser String
cell = quotedCell <|> many (noneOf ",\n\r")

quotedCell :: Parser String
quotedCell = 
    do _ <- char '"'
       content <- many quotedChar
       _ <- char '"' <?> "quote at end of cell"
       return content

quotedChar :: Parser Char
quotedChar =
        noneOf "\""
    <|> try (string "\"\"" >> return '"')

eol :: Parser String
eol =   try (string "\n\r")
    <|> try (string "\r\n")
    <|> string "\n"
    <|> string "\r"
    <?> "end of line"

parseCSV :: ByteString -> Either ParseError [[String]]
parseCSV = parse csvFile "(unknown)"

main :: IO ()
main =
    do c <- ByteString.getContents
       case parse csvFile "(stdin)" c of
            Left e -> do Prelude.putStrLn "Error parsing input:"
                         print e
            Right r -> mapM_ print r

しかし、これは機能するのに非常に些細なことだったので、おそらくあなたが望むものにはなり得ないと思います。おそらく、すべてをずっとそのままにしておきたいですByteString[Word8]?したがって、以下の私の2回目の試み。私はまだ実行中importですがText.Parsec.ByteString、これは間違いである可能性があり、コードは絶望的に変換でいっぱいです。

しかし、それはコンパイルされ、完全な型注釈を持っているので、健全な出発点になるはずです。

import Prelude hiding (getContents, putStrLn)
import Data.ByteString (ByteString)
import Control.Monad (liftM)

import qualified Prelude (getContents, putStrLn)
import qualified Data.ByteString as ByteString (pack, getContents)
import qualified Data.ByteString.Char8 as Char8 (pack)

import Data.Word (Word8)
import Data.ByteString.Internal (c2w)

import Text.Parsec ((<|>), (<?>), parse, try, endBy, sepBy, many)
import Text.Parsec.ByteString
import Text.Parsec.Prim (tokens, tokenPrim)
import Text.Parsec.Pos (updatePosChar, updatePosString)
import Text.Parsec.Error (ParseError)

csvFile :: Parser [[ByteString]]
csvFile = endBy line eol
line :: Parser [ByteString]
line = sepBy cell (char ',')
cell :: Parser ByteString
cell = quotedCell <|> liftM ByteString.pack (many (noneOf ",\n\r"))

quotedCell :: Parser ByteString
quotedCell = 
    do _ <- char '"'
       content <- many quotedChar
       _ <- char '"' <?> "quote at end of cell"
       return (ByteString.pack content)

quotedChar :: Parser Word8
quotedChar =
        noneOf "\""
    <|> try (string "\"\"" >> return (c2w '"'))

eol :: Parser ByteString
eol =   try (string "\n\r")
    <|> try (string "\r\n")
    <|> string "\n"
    <|> string "\r"
    <?> "end of line"

parseCSV :: ByteString -> Either ParseError [[ByteString]]
parseCSV = parse csvFile "(unknown)"

main :: IO ()
main =
    do c <- ByteString.getContents
       case parse csvFile "(stdin)" c of
            Left e -> do Prelude.putStrLn "Error parsing input:"
                         print e
            Right r -> mapM_ print r

-- replacements for some of the functions in the Parsec library

noneOf :: String -> Parser Word8
noneOf cs   = satisfy (\b -> b `notElem` [c2w c | c <- cs])

char :: Char -> Parser Word8
char c      = byte (c2w c)

byte :: Word8 -> Parser Word8
byte c      = satisfy (==c)  <?> show [c]

satisfy :: (Word8 -> Bool) -> Parser Word8
satisfy f   = tokenPrim (\c -> show [c])
                        (\pos c _cs -> updatePosChar pos c)
                        (\c -> if f (c2w c) then Just (c2w c) else Nothing)

string :: String -> Parser ByteString
string s    = liftM Char8.pack (tokens show updatePosString s)

おそらくあなたの懸念は、効率の観点から、とByteString.packの定義におけるこれらの2つの指示であるべきです。Text.Parsec.ByteStringモジュールを置き換えて、「厳密なByteStringsをトークンタイプのインスタンスにする」代わりに、ByteStringsをトークンタイプのインスタンスにすることもできますが、これは効率の向上には役立ちません。エラーメッセージの入力での位置を追跡するために、すべてのsourcePos関数を再実装しようとすると頭が痛くなるだけです。cellquotedCellStreamCharStreamWord8

いいえ、それをより効率的にする方法は、のタイプ、およびのタイプとcharquotedCharおよびのタイプをそれぞれ変更することです。タイプをに変更することもできます。必要な変更は次のようになります。stringParser [Word8]linecsvFileParser [[Word8]]Parser [[[Word8]]]eolParser ()

cell :: Parser [Word8]
cell = quotedCell <|> many (noneOf ",\n\r")

quotedCell :: Parser [Word8]
quotedCell = 
    do _ <- char '"'
       content <- many quotedChar
       _ <- char '"' <?> "quote at end of cell"
       return content

string :: String -> Parser [Word8]
string s    = [c2w c | c <- (tokens show updatePosString s)]

c2w効率に関する限り、すべての呼び出しについて心配する必要はありません。費用がかからないからです。

これで質問に答えられない場合は、どうなるかを言ってください。

于 2013-03-22T17:00:44.710 に答える
0

私はそうは思わない。を使用して自分で作成する必要がありますtokens。ドキュメントは少し...存在しませんが、最初の2つの引数は、エラーメッセージで予期されるトークンを表示するために使用する関数と、エラーで出力されるソース位置を更新するための関数です。

于 2013-03-16T22:30:00.970 に答える