https://www.fpcomplete.com/school/starting-with-haskell/libraries-and-frameworks/text-manipulation/attoparsec给出的解析器似乎可以正常工作,但是有问题。
代码(此处重复)为:
{-# LANGUAGE OverloadedStrings #-}
-- This attoparsec module is intended for parsing text that is
-- represented using an 8-bit character set, e.g. ASCII or ISO-8859-15.
import Data.Attoparsec.Char8
import Data.Word
-- | Type for IP's.
data IP = IP Word8 Word8 Word8 Word8 deriving Show
parseIP :: Parser IP
parseIP = do
d1 <- decimal
char '.'
d2 <- decimal
char '.'
d3 <- decimal
char '.'
d4 <- decimal
return $ IP d1 d2 d3 d4
main :: IO ()
main = print $ parseOnly parseIP "131.45.68.123"
如果解析器输入了无效的IP地址(例如“ 1000.1000.1000.1000”),则由于强制数字转换,它不会失败并返回垃圾结果。
有简单的方法可以解决此问题吗?一种方法是使用较大的Word
类型,例如,Word32
并检查数字是否小于256。但是,如果输入是病理性的,即使返回的数也可能会返回垃圾(例如Word32
,也发生溢出)。转换为Integer
似乎是一个选择,因为它是不受限制的,但是同样,对抗性输入可能会使程序用尽内存。
那么,一个避免这些问题的(非常优雅的)解析器会是什么样呢?
我对您的问题的理解是,您不仅要在输入数字太大时失败,而且也不想让解析器消耗比所需更多的输入。
我们可以定义一个函数来解析最大整数,否则将失败:
import Data.Attoparsec.ByteString.Char8
import Data.Word
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Control.Applicative
import Data.List (foldl')
import Control.Monad
decimalMax :: Integral a => Integer -> Parser a
decimalMax dMax = do
let numDigs = ceiling $ log (fromIntegral(dMax+1)) / log 10
getVal = foldl' (\s d -> s*10+fromIntegral (d-48)) 0 . B.unpack
val <- getVal <$> scan 0 (\n c ->
if n > numDigs || not (isDigit c) then Nothing else Just (n+1))
if val <= dMax
then return $ fromIntegral val
else fail $ "decimalMax: parsed decimal exceeded" ++ show dMax
此函数计算最大位数中的位数,然后仅消耗最多位数。您的IP地址解析器几乎保持不变:
parseIP :: Parser IP
parseIP = IP <$> dd <*> dd <*> dd <*> dig where
dig = decimalMax 255
dd = dig <* char '.'
main :: IO ()
main = do
print $ parseOnly parseIP "131.45.68.123"
print $ parseOnly parseIP "1000.1000.1000.1000"
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句