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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:56:46