Haskell parsec中输入的意外结束

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

我想解析这样的文件:

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 <- 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