使用attoparsec解析n个十六进制数字

时间:2015-04-22 23:50:53

标签: haskell attoparsec

好的,所以我需要解析十六进制的数字,我遇到了一个问题,我无法停止标准的attoparsec十六进制解析器hexadecimal

我的第一个想法是:

nHex n = take n *> hexadecimal但这不起作用,因为它取出4位数然后解析字符串xD的其余部分

下一个有用的想法是:

hex :: (Num a, Eq a) => Int -> Parser a
hex n = fst . head . readHex <$> count n (satisfy isHexDigit)

但该代码的问题出在attoparsec库中,它警告不要为速度问题返回字符列表,这个十六进制解析器是我整个程序的基础

尝试提高速度的下一个想法是:

parseFragments :: (Bits a, Integral a) => Int -> Parser a
parseFragments n = do
      fourChars <- B.take n
      let hexDigits = parseOnly hexadecimal fourChars
      case hexDigits of  
              Left err -> fail err
              Right x  -> return x

但是使用parseOnly感觉就像是一个可怕的黑客。 是否有一种比较惯用的快速方式?

1 个答案:

答案 0 :(得分:1)

Data.Attoparsec.ByteString.Char8.hexadecimalimplemented as

hexadecimal :: (Integral a, Bits a) => Parser a
hexadecimal = B8.foldl' step 0 `fmap` I.takeWhile1 isHexDigit
  where
    isHexDigit w = (w >= 48 && w <= 57) ||
                   (w >= 97 && w <= 102) ||
                   (w >= 65 && w <= 70)
    step a w | w >= 48 && w <= 57  = (a `shiftL` 4) .|. fromIntegral (w - 48)
             | w >= 97             = (a `shiftL` 4) .|. fromIntegral (w - 87)
             | otherwise           = (a `shiftL` 4) .|. fromIntegral (w - 55)

您可以使用几乎相同的内容,除了您需要对take的结果进行插入,因为您的某些字符可能不是有效的十六进制字符。您可以使用(Maybe a -> Word8 -> Maybe a)将两者放在同一个函数中,但为了简单起见,我使用了上面的函数:

fixedHexadecimal :: (Integral a, Bits a) => Int -> Parser a
fixedHexadecimal n = do
    bytes <- A.take n
    if B8.all isHexDigit bytes 
      then B8.foldl' step 0 bytes
      else fail "fixedHexadecimal"

  where isHexDigit = -- see above
        step       = -- see above