gpt4 book ai didi

parsec - 以秒差距匹配字节串

转载 作者:行者123 更新时间:2023-12-01 23:25:05 26 4
gpt4 key购买 nike

我目前正在尝试使用现实世界 Haskell 中提供的完整 CSV 解析器。为了我尝试修改代码以使用 ByteString 而不是 String,但是有一个 string 组合器仅适用于 字符串

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

我已经看到有一个处理 ByteString 的替代解析器:attoparsec,但我更愿意坚持使用 Parsec,因为我刚刚学习如何使用它。

最佳答案

我假设你从类似的事情开始

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

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

我一路上注意到的事情:

  • 如果您导入 Text.Parsec.ByteString,那么它会使用 Data.ByteString.Char8 中的 uncons,而 Data.ByteString.Char8 又会使用 Data 中的 w2c .ByteString.Internal,将所有读取的字节转换为 Char。这使得 Parsec 的行号和列号错误报告能够正常工作,并且还使您能够毫无问题地使用 string 和 friend 。

因此,简单版本的 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] 或类似的东西?因此我在下面进行了第二次尝试。我仍在导入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)

从效率角度来看,您可能关心的是 cellquotedCell 定义中的那两条 ByteString.pack 指令。您可能会尝试替换 Text.Parsec.ByteString 模块,以便不再“使严格的 ByteStrings 成为具有 Char 标记类型的 Stream 实例”,而是使 ByteStrings 成为StreamWord8 token 类型,但这不会帮助您提高效率,只会让您在尝试重新实现所有 sourcePos 函数来跟踪您的位置时感到头疼在错误消息的输入中。

不,提高效率的方法是将 charquotedCharstring 的类型更改为 Parser [Word8] 以及 linecsvFileParser [[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 的所有调用,因为它们不需要任何成本。

如果这不能回答您的问题,请说出什么可以回答您的问题。

关于parsec - 以秒差距匹配字节串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15455084/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com