【问题标题】:How to parse Number with comma via Megaparsec如何通过 Megaparsec 解析带逗号的数字
【发布时间】:2022-12-06 07:46:06
【问题描述】:

目前我有一个解析器:

pScientific :: Parser Scientific
pScientific = lexeme L.scientific

这能够轻松解析类似 4087.00 的内容

但是当编号4,087.00 时失败有没有办法让 megaparsec 用逗号解析数字?

PS:我是 haskell 的新手,如果这是一个愚蠢的问题,请道歉

【问题讨论】:

  • 之所以不解析这个是因为这个类型主要是为JSON解析而定义的,而JSON不允许这样,数组和对象中的元素用逗号分隔。

标签: haskell parsec megaparsec


【解决方案1】:

如果你的解析器的其余部分没有逗号,一个廉价而愉快的解决方案是在解析之前简单地删除它们。

如果您确实需要在解析过程中保留逗号,那么您最好的选择是查找 scientific 的源代码,复制、粘贴和调整——我不知道有没有预制的解析器接受逗号。

【讨论】:

    【解决方案2】:

    之所以不解析这个是因为scientific类型主要是为JSON解析而定义的,而JSON不允许这样,在数组和对象中用逗号分隔元素。

    我们可以看看implementation of scientific [src]

    -- | Parse a JSON number.
    scientific :: Parser Scientific
    scientific = do
      sign <- A.peekWord8'
      let !positive = not (sign == W8_MINUS)
      when (sign == W8_PLUS || sign == W8_MINUS) $
        void A.anyWord8
    
      n <- decimal0
    
      let f fracDigits = SP (B.foldl' step n fracDigits)
                            (negate $ B.length fracDigits)
          step a w = a * 10 + fromIntegral (w - W8_0)
    
      dotty <- A.peekWord8
      SP c e <- case dotty of
                  Just W8_DOT -> A.anyWord8 *> (f <$> A.takeWhile1 isDigit_w8)
                  _           -> pure (SP n 0)
    
      let !signedCoeff | positive  =  c
                       | otherwise = -c
    
      (A.satisfy (ex -> case ex of W8_e -> True; W8_E -> True; _ -> False) *>
          fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
        return (Sci.scientific signedCoeff    e)
    {-# INLINE scientific #-}
    

    要更改的主要内容是 decimal0 部分,它捕获零个或多个十进制数的序列。例如,我们可以通过以下方式实现:

    import qualified Data.ByteString as B
    
    decimal0' :: Parser Integer
    decimal0' = do
      digits <- B.filter (x -> x /= 44) <$> A.takeWhile1 (x -> isDigit_w8 x || x == 44)
      if B.length digits > 1 && B.unsafeHead digits == 48
        then fail "leading zero"
        else return (bsToInteger digits)
    

    然后使用那个:

    import qualified Data.Attoparsec.ByteString as A
    import qualified Data.Scientific as Sci
    import Data.Attoparsec.ByteString.Char8 (isDigit_w8)
    
    -- | Parse a JSON number.
    scientific :: Parser Scientific
    scientific = do
      sign <- A.peekWord8'
      let !positive = not (sign == 45)
      when (sign == 43 || sign == 45) $
        void A.anyWord8
    
      n <- decimal0'
    
      let f fracDigits = SP (B.foldl' step n fracDigits)
                            (negate $ B.length fracDigits)
          step a w = a * 10 + fromIntegral (w - W8_0)
    
      dotty <- A.peekWord8
      SP c e <- case dotty of
                  Just 46 -> A.anyWord8 *> (f <$> A.takeWhile1 isDigit_w8)
                  _           -> pure (SP n 0)
    
      let !signedCoeff | positive  =  c
                       | otherwise = -c
    
      (A.satisfy (ex -> case ex of W8_e -> True; W8_E -> True; _ -> False) *>
          fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
        return (Sci.scientific signedCoeff    e)
    {-# INLINE scientific' #-}
    

    这没有考虑到逗号是在每三位数字之后放置的,因此这将需要额外的逻辑,但这是在 Scientific 的组成部分中接受逗号的基本实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      • 1970-01-01
      相关资源
      最近更新 更多