You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell Parsec十六进制字符串解析器不符合预期问题排查与修复

问题原因分析

你的解析器会在遇到非法字符时返回部分匹配结果,核心原因是Parsec的默认行为是匹配输入的前缀部分,不会自动检查整个输入是否被完全消耗。

拿第三次测试的#xcafexbeef来说,解析器的执行流程是:

  1. 成功匹配开头的#x;
  2. 用many1 hexDigit匹配到cafe(都是合法十六进制字符),直到遇到第一个非十六进制字符x就停止匹配;
  3. 直接返回解析结果,完全忽略了后面的xbeef部分——因为你的解析规则没有要求必须读到输入的结尾。
修复方案

要让解析器在存在任何无效字符时直接判定不匹配,只需要在parseHex的末尾添加eof解析器,强制要求整个输入必须被完全解析,不能有剩余字符。

eof是Parsec提供的内置解析器,它会检查当前是否已经到达输入的末尾,如果还有未解析的字符,就会抛出解析错误。

修改后的完整代码如下:

module Main where
import Control.Monad
import Numeric
import System.Environment
import Text.ParserCombinators.Parsec hiding (spaces)

parseHex :: Parser Integer
parseHex = do
  string "#x"
  x <- many1 hexDigit
  eof  -- 新增:确保输入被完全消耗,无剩余字符
  return (fst (head (readHex x)))

testHex :: String -> String
testHex input = case parse parseHex "lisp" input of
  Left err -> "Does not match " ++ show err
  Right val -> "Matched " ++ show val  -- 加个空格让输出更美观

main :: IO ()
main = do
  args <- getArgs
  putStrLn (testHex (head args))
测试验证

现在重新测试第三次用例,结果就符合预期了:

*Main> testHex "#xcafexbeef"
"Does not match \"lisp\" (line 1, column 7):\nunexpected \"x\"\nexpecting end of input"

解析器因为输入未被完全消耗,直接返回错误,不再返回部分匹配的结果。

另外前两个测试用例依然正常工作:

*Main> testHex "#xcafebeef"
"Matched 3405692655"
*Main> testHex "#xnothx"
"Does not match \"lisp\" (line 1, column 3):\nunexpected \"n\"\nexpecting hexadecimal digit"

内容的提问来源于stack exchange,提问作者Bhargav Kulkarni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 14:57:37