5

我想解析这样的文件:

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

但是在运行时,它会抛出异常:

ts <- getTestData "data" 
*** Exception: "test data" (line 11, column 1):
unexpected end of input
expecting natural or "\n"

我不明白,为什么它说第 11 行,而我的 data.test 文件只有 10 行。所以我在几次尝试后都没有解决这个问题。

4

4 回答 4

5

我最好的猜测是whiteSpaceinline正在消耗换行符。因此,您的整个文件正在由单个line解析器解析,并且eol解析器永远没有机会获得"\n". 尝试替换whiteSpacemany (char ' ')看看是否有帮助。

于 2011-06-04T07:29:08.843 回答
4

这是一个使用原始字符解析器而不是令牌解析器的工作实现。注意 - 不使用空格作为分隔符更健壮,但如果存在则将其删除。(<*)如果您从 Applicative 中使用,我使用单行 do-notation 的位会更整洁。

{-# 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
于 2011-06-04T11:20:56.163 回答
1

我敢打赌,您在最后一行的末尾缺少换行符。对于解析完整的行,它应该是“861:1\n”,但它可能是“861:1EOF”。所以我认为你的解析器正确地识别出你的输入是不正确的。

于 2011-06-04T09:00:56.630 回答
0

实际上,我发现您可以使用 whiteSpace(例如,轻松忽略多行块注释),同时仍然是面向行的。当您需要换行符时,只需包含此解析器。

col (== 1) "only matches beginning of line"

col pred errStr = do
  c <- sourceColumn <$> getPosition
  if pred c then return ()
            else unexpected errStr
于 2013-10-04T11:53:44.897 回答