2011-11-30 108 views
3

作为学习练习,我使用parsec在测试文件中查找值。我通常使用regexp来处理这个特定的情况,但是想看看parsec是否也有意义。不幸的是,我遇到了一些问题。使用parsec从文本文件中提取数据

数据文件由重复的部分组成,其外观类似于以下内容。 'SHEF'是页面之间的六个值之一和变化,我想用它来构造数据类型。

Part A SHEF Nov/14/2011 (10:52) 
     ------------------- 
     Portfolio Valuation 
     ------------------- 

     FOREIGN COMMON STOCK       6,087,152.65 
     FOREIGN COMMON STOCK - USA      7,803,858.84 
     RIGHTS             0.00 

我构建量的数据类型中的每个资产类别:

type Sector = String 
type Amount = Double 
type FundCode = String 

data SectorAmount = SectorAmount (Sector,Amount) deriving (Show, Eq) 

data FundSectors = FundSectors { 
     fund    :: FundCode 
    , sectorAmounts :: [SectorAmount] 
     } deriving (Show, Eq) 

我的代码,该代码编译成功,是如下所示。它解析文件并正确检索每个资产类中的值,但我永远无法在fundValue解析器中正确设置状态。我已经用输入字符串测试了fundValue解析器,并且它成功解析了它,但由于某种原因,行函数并不按照我认为的方式工作。我希望它在文件中寻找以“Part A”开头的行,找到代码并将其存储在状态中,以便标记解析器成功解析行时供以后使用。

是否使用fail导致该问题?

allocationParser :: String -> Either ParseError [FundSectors] 
allocationParser input = do 
       runParser allocationFile "" "" input 


allocationFile :: GenParser Char FundCode [FundSectors] 
allocationFile = do 
     secAmt <- many line 
     return secAmt 


line :: GenParser Char FundCode FundSectors 
line = try (do fund <- try fundValue 
       eol 
       fail "") 
     <|> do result <- try tag 
       eol 
       f <- getState 
       return $ FundSectors {fund=f, sectorAmounts = [result]} 


fundValue :: GenParser Char FundCode FundCode 
fundValue = do manyTill anyChar . try $ lookAhead (string "Part A ") 
       string "Part A " 
       fCode <- try fundCode 
       setState fCode 
       v <- many (noneOf "\n\r") 
       eol 
       return fCode 


fundCode :: GenParser Char FundCode String 
fundCode = try (string "SHSF") 
     <|> try (string "SHIF") 
     <|> try (string "SHFF") 
     <|> try (string "SHEF") 
     <|> try (string "SHGE") 
     <|> try (string "SHSE") 
     <|> fail "Couldn't match fundCode" 


tag :: GenParser Char FundCode SectorAmount 
tag = do manyTill anyChar . try $ lookAhead tagName 
     name <- tagName 
     v <- many (noneOf "\n\r") 
     let value = read ([x | x <- v, x /= ',']) :: Double -- remove commas from currency 
     return $ SectorAmount (name,value) 

eol :: GenParser Char FundCode String 
eol = try (string "\n\r") 
    <|> try (string "\r\n") 
    <|> string "\n" 
    <|> string "\r" 
    <|> fail "Couldn't find EOL" 

在此先感谢。

回答

1

是的,“try fundValue”块中的失败撤消了setState。 您需要稍微重新设计解析器,但看起来很接近。

+0

谢谢克里斯,这很有道理。我认为我挣扎的是,当我解析一条线时,我想要在设置状态并返回任何内容时获取一个fundValue,或者找到一个标记并返回一个数据类型,否则都不会。我想我可以使用'Maybe FundCode'并在找到fundValue时返回'Nothing',但这看起来效率很低,因为'many line'会导致一个包含很多Nothing值的列表。我希望使用失败能够解决问题 - 有没有其他方法可以与'many'一起使用,以便我可以设置状态但不会返回值? – Neil

+0

@Neil:这些'Nothing'值几乎不会花费任何东西,并且几乎在你用'(fmap catMaybes <$>多行)创建它们时'' – sclv

+0

好点,谢谢@sclv,我会试一试。 – Neil