匹配Parsec中的字节串

时间:2013-03-16 21:49:27

标签: parsec bytestring haskell

我目前正在尝试使用 Real World Haskell 中提供的Full CSV Parser。我试图修改代码以使用ByteString代替String,但有一个string组合器可以与String一起使用。

Parsec组合器是否与string类似,可以与ByteString一起使用,而无需来回转换?

我见过有一个替代解析器可以处理ByteStringattoparsec,但我更愿意坚持使用Parsec,因为我只是在学习如何使用它。

2 个答案:

答案 0 :(得分:5)

我假设你是从

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

这是我到目前为止所得到的。有两个版本。都编译。可能两者都不是你想要的,但它们应该有助于讨论并帮助你澄清你的问题。

我一路上注意到的事情:

  • 如果你import Text.Parsec.ByteString那么它使用来自Data.ByteString.Char8的uncons,后者又使用Data.ByteString.Internal中的w2c将所有读取的字节转换为{{1} }}秒。这使得Parsec的行号和列号错误报告能够合理地工作,并且还使您可以毫无问题地使用Char和朋友。

因此,简单版本的CSV解析器就是这样做的:

string

但是这种工作是如此微不足道,我认为它不可能是你想要的。也许您希望一切都保持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] 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的定义中,效率方面的问题可能应该是那两条cell说明。您可能会尝试替换Text.Parsec.ByteString模块,以便“使用quotedCell令牌类型使Stream的实例成为严格的ByteStrings”,而是使ByteStrings成为Char的实例。 Stream令牌类型,但这不会帮助您提高效率,只会让您头疼,尝试重新实现所有sourcePos函数以跟踪您在错误消息输入中的位置。

不,提高效率的方法是将Word8charquotedChar的类型更改为string以及{{1}的类型}和Parser [Word8]分别为linecsvFile。您甚至可以将Parser [[Word8]]的类型更改为Parser [[[Word8]]]。必要的变化看起来像这样:

eol

就效率而言,您无需担心Parser ()的所有电话,因为它们不需要任何费用。

如果这不能回答你的问题,请说明会是什么。

答案 1 :(得分:0)

我不相信。您需要使用tokens自己创建一个。虽然它的文档有点......不存在,但前两个参数是用于在错误消息中显示预期标记的函数,以及用于更新将在错误中打印的源位置的函数。