私はあなたが次のようなものから始めていると思います
import Prelude hiding (getContents, putStrLn)
import Data.ByteString
import Text.Parsec.ByteString
これが私がこれまでに得たものです。2つのバージョンがあります。両方ともコンパイルします。おそらくどちらもあなたが望むものではありませんが、それらは議論を助け、あなたの質問を明確にするのに役立つはずです。
途中で気付いたことがあります。
import Text.Parsec.ByteString
次に、これはuncons
from Data.ByteString.Char8を使用し、次にfrom w2c
Data.ByteString.Internalを使用して、すべての読み取りバイトをChar
sに変換します。これにより、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関数を再実装しようとすると頭が痛くなるだけです。cell
quotedCell
Stream
Char
Stream
Word8
いいえ、それをより効率的にする方法は、のタイプ、およびのタイプとchar
、quotedChar
およびのタイプをそれぞれ変更することです。タイプをに変更することもできます。必要な変更は次のようになります。string
Parser [Word8]
line
csvFile
Parser [[Word8]]
Parser [[[Word8]]]
eol
Parser ()
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
効率に関する限り、すべての呼び出しについて心配する必要はありません。費用がかからないからです。
これで質問に答えられない場合は、どうなるかを言ってください。