【问题标题】:using parsec to pick data out of text file使用 parsec 从文本文件中提取数据
【发布时间】:2011-11-30 21:28:46
【问题描述】:

作为一项学习练习,我使用 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 解析器,它确实成功地解析了它,但由于某种原因,line 函数没有按照我想象的方式工作。我希望它在文件中查找以“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"

提前致谢。

【问题讨论】:

    标签: haskell parsec


    【解决方案1】:

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

    【讨论】:

    • 谢谢克里斯,这是有道理的。我认为我正在努力解决的是,当我解析一行时,我要么想在设置状态时获得一个 fundValue 并且什么都不返回,要么找到一个标签并返回一个数据类型,或者两者都不做。我想我可以使用Maybe FundCode 并在找到fundValue 时返回Nothing,但这似乎效率低下,因为many line 将导致一个包含很多Nothing 值的列表。我希望使用 fail 可以解决问题 - 有没有其他方法可以使用 many 以便我可以设置状态但不返回值?
    • @Neil:那些Nothing 值几乎没有成本,您几乎可以在使用(fmap catMaybes &lt;$&gt; many line) 创建它们后立即丢弃它们
    猜你喜欢
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 2018-06-14
    • 2011-04-20
    • 1970-01-01
    相关资源
    最近更新 更多