Haskell新手求助:如何用Attoparsec递归提取TXT文件所有单词?
解决Attoparsec只解析第一个单词的问题
嘿,刚上手Haskell和Attoparsec对吧?你的问题很典型——Attoparsec默认只会从输入开头匹配一次你定义的解析规则,不会自动遍历整个文本。从你说的只得到Prose {word = "Hello"}来看,大概率是你的解析器只定义了单个单词的解析逻辑,没告诉它要重复解析所有单词。
我来给你拆解一下解决方案:
1. 先完善单个单词的解析
首先得处理英文里的各种单词情况,比如带撇号的I'm、带点号的Mr.Robot,还要把单词后面的标点(逗号、句号)排除在外。我们可以这么写:
{-# LANGUAGE OverloadedStrings #-} import Data.Attoparsec.Text import Data.Text (Text) import Data.Char (isAlpha, isSpace) -- 你的Prose数据类型 data Prose = Prose { word :: Text } deriving (Show) -- 匹配单词字符:字母、撇号、点号(处理缩写/尊称) wordChar :: Char -> Bool wordChar c = isAlpha c || c == '\'' || c == '.' -- 解析单个单词 wordParser :: Parser Text wordParser = takeWhile1 wordChar -- 解析单词之间的分隔符:空格、换行、标点符号 separator :: Parser () separator = skipMany $ satisfy (\c -> isSpace c || c `elem` [',', '.', '!', '?'])
2. 关键:重复解析所有单词
这是你原来代码缺失的部分——用many让解析器重复匹配所有单词,同时处理分隔符:
-- 解析整个文本的所有单词 proseParser :: Parser [Prose] proseParser = do separator -- 跳过文本开头的空白/标点(如果有的话) many $ do w <- wordParser separator -- 跳过当前单词后的所有分隔符 return $ Prose w
3. 测试解析逻辑
用你的示例文本测试一下:
sampleText :: Text sampleText = "Hello World, I am Elliot Anderson. \nAnd I'm Mr.Robot.\n" main :: IO () main = case parseOnly proseParser sampleText of Left err -> putStrLn $ "解析错误:" ++ err Right proseList -> print proseList
运行这段代码,你会得到所有单词的列表:
[Prose {word = "Hello"}, Prose {word = "World"}, Prose {word = "I"}, Prose {word = "am"}, Prose {word = "Elliot"}, Prose {word = "Anderson"}, Prose {word = "And"}, Prose {word = "I'm"}, Prose {word = "Mr.Robot"}]
为什么原来的代码只解析了第一个单词?
Attoparsec的解析器是单次匹配的——如果你只写了Prose <$> wordParser,它只会匹配输入里第一个符合wordParser的内容,然后就停止了。必须用many(匹配0或多次)或者some(匹配1或多次)来告诉它要遍历整个输入,重复解析所有符合规则的单词。
另外,别忘了处理分隔符——如果不跳过单词之间的空格、标点,解析器会在第一个单词后遇到逗号/空格,直接抛出匹配失败的错误。
内容的提问来源于stack exchange,提问作者centrinok
相关产品推荐
相关产品推荐

