Haskell parsec中输入的意外结束
我想解析这样的文件: 66:3 3:4 329:2 101:3 495:4 55:5 268:5 267:2 242:4 262:1 861:1 66:3 3:4 329:2 101:3 495:4 55:5 268:5 267:2 242:4 262:1 861:1 我的代码如下所示:Haskell parsec中输入的意外结束,haskell,parsec,Haskell,Parsec,我想解析这样的文件: 66:3 3:4 329:2 101:3 495:4 55:5 268:5 267:2 242:4 262:1 861:1 66:3 3:4 329:2 101:3 495:4 55:5 268:5 267:2 242:4 262:1 861:1 我的代码如下所示: getTestData :: String -> IO [[(Int, Int)]] getTestData name = do --res <- pars
getTestData :: String -> IO [[(Int, Int)]]
getTestData name = do
--res <- parseFromFile testData (name ++ ".test")
fc <- readFile (name ++ ".test")
let res = parse testData "test data" fc
case res of
Left e -> error $ show e-- "test data parse eror."
Right ts -> return ts
eol = char '\n'
testData = endBy line eol
--testData = many line
testTuple = do
i <- natural
colon
r <- natural
return (fromIntegral i:: Int, fromIntegral r:: Int)
line = sepBy testTuple whiteSpace
getTestData::String->IO[[(Int,Int)]]
getTestData name=do
--res返回ts
eol=char'\n'
testData=结束行下线
--testData=多行
testTuple=do
我我最好的猜测是行中的空格
正在使用换行符。因此,您的整个文件正由一个行解析器进行解析,而eol
解析器永远没有机会获得“\n”
。尝试用many(char'')
替换whiteSpace
,看看这是否有帮助。我打赌您在最后一行的末尾缺少一个换行符。
要分析完整的行,它应该是“861:1\n”,但可能是“861:1EOF”。
因此,我认为解析器正确地识别了不正确的输入 这是一个使用原始字符解析器而不是令牌解析器的有效实现。注意-不使用空格作为分隔符更为稳健,如果存在,则删除它。如果使用(IO[[(Int,Int)],我使用的一行do表示法的位要整洁得多
getTestData name=do
--res返回ts
testData::解析器[[(Int,Int)]]
测试数据=输入
输入::解析器[[(Int,Int)]]
输入=多(做{一行}
testTuple::解析器(Int,Int)
testTuple=do
实际上,我发现您可以使用空格(例如,可以很容易地忽略多行块注释),同时仍然是面向行的
col (== 1) "only matches beginning of line"
col pred errStr = do
c <- sourceColumn <$> getPosition
if pred c then return ()
else unexpected errStr
col(=1)“仅匹配行首”
col pred errStr=do
c您找到的是文件的结尾而不是行的结尾。还要注意,解析器中使用的默认空格组合符使用新行,因此您不希望testData
使用eol
作为endBy
条件。可能使用testData=many1行
可以工作,但一般来说是这样的l即使对于简单格式,您也必须非常小心处理空格。Parsec是为解析编程语言而不是面向行的数据文件而构建的,因此它将所有空格视为同一事物,而不是区分换行符或其他任何内容。当使用多个空格时,它会这样抱怨:**Exception:Text.ParserCombinators.Parsec.Prim.many:combinator“many”应用于接受空字符串的解析器。如果将line
转换为line=sepBy1 testTuple whiteSpace
?(尽管这是一个关于空格的不可靠的过程)事实上,我错误地建议使用line=sepBy1 testTuple whiteSpace
——您不能使用whiteSpace
编写面向行的解析器。在这种情况下,因为您需要面向行的解析器,并且您的格式很简单,所以最好使用Text.parsercompbinators.Parsec.Char来创建基本解析器,而不是使用使用Text.parsercompbinators.Parsec.token
中的令牌解析器是一个很好的选择。但是,您需要编写自己版本的natural
。这不会有帮助-问题是natural
和colon
会消耗任何尾随空格,因此testTuple解析器已经消耗了任何换行符。对于空格敏感度ve解析器您需要在Text.Parsec.Token
中实现自己版本的解析器。当将空格更改为多(字符“”)时,会引发类似的异常:**异常:“测试数据”(第11行,第1列):输入意外结束,预期为“”,自然或“\n”我猜解析器在第11行找到了一个eof,因此认为它是意外的。*Main>readFile“data.test”“66:3 3:4\n329:2\n101:3\n495:4\n55:5\n268:5\n267:2\n242:4\n262:1\n861:1\n”事实并非如此。谢谢你的回答。效果很好。我根据你的代码重写了我的代码,并真正发现我原始代码中的所有自然、空格和行都需要更改为你的代码才能正常工作。我还弄明白了为什么我的原始“行”定义不起作用。因为我的测试文件有尾随空格,解析器猜测它们e将是其他元组。
{-# OPTIONS -Wall #-}
module ParsecWhite where
import Text.ParserCombinators.Parsec
import Data.Char
main = getTestData "sample"
getTestData :: String -> IO [[(Int, Int)]]
getTestData name = do
--res <- parseFromFile testData (name ++ ".test")
fc <- readFile (name ++ ".test")
let res = parse testData "test data" fc
case res of
Left e -> error $ show e -- "test data parse eror."
Right ts -> return ts
testData :: Parser [[(Int,Int)]]
testData = input
input :: Parser [[(Int,Int)]]
input = many (do { a <- line; newline; return a })
<?> "input"
line :: Parser [(Int,Int)]
line = many (do { a <- testTuple; softWhite; return a}) <?> "line"
testTuple :: Parser (Int,Int)
testTuple = do
i <- natural
colon
r <- natural
return (i,r)
<?> "testTuple"
softWhite :: Parser ()
softWhite = many (oneOf " \t") >> return ()
colon :: Parser ()
colon = char ':' >> return ()
natural :: Parser Int
natural = fmap (post 0) $ many1 digit
where
post ac [] = (ac * 10)
post ac [x] = (ac * 10) + digitToInt x
post ac (x:xs) = post ((ac * 10) + digitToInt x) xs
col (== 1) "only matches beginning of line"
col pred errStr = do
c <- sourceColumn <$> getPosition
if pred c then return ()
else unexpected errStr