首页 > 解决方案 > 使用 attoparsec 递归返回 .txt 文件中的所有单词

问题描述

我对 Haskell 相当陌生,我刚刚开始学习如何使用 attoparsec 从 .txt 文件中解析大量英文文本。我知道如何在不使用 attoparsec 的情况下获取 .txt 文件中的单词数,但我有点坚持使用 attoparsec。当我在下面运行我的代码时,假设

“Hello World,我是 Elliot Anderson。\n我是 Mr.Robot。\n”

我只回来了:

世界,我是艾略特·安德森。\n我是 Mr.Robot。\n" (散文 {word = "Hello"})

这是我当前的代码:

{-# LANGUAGE OverloadedStrings #-}
import Control.Exception (catch, SomeException)
import System.Environment (getArgs)
import Data.Attoparsec.Text
import qualified Data.Text.IO as Txt
import Data.Char
import Control.Applicative ((<*>), (*>), (<$>), (<|>), pure)

{-
This is how I would usually get the length of the list of words in a .txt file normally.

countWords :: String -> Int
countWords input = sum $ map (length.words) (lines input)

-}

data Prose = Prose {
  word :: String
} deriving Show

prose :: Parser Prose
prose = do
  word <- many' $ letter
  return $ Prose word

main :: IO()
main = do
  input <- Txt.readFile "small.txt"
  print $ parse prose input

另外,以后如何获得单词的整数计数?此外,有关如何开始使用 attoparsec 的任何建议?

标签: parsinghaskellattoparsec

解决方案


你已经有了一个很好的开始——你可以解析一个单词。
接下来你需要的是 a Parser [Prose],它可以通过将你的prose解析器与另一个消耗“非散文”部分的解析器组合来表达,使用sepByor ,你可以在文档sepBy1中查找。Data.Attoparsec.Text

从那里,获取字数的最简单方法是简单地获取您所获得的长度[Prose]

编辑:

这是一个最小的工作示例。Parserrunner 已被交换parseOnly以允许忽略剩余输入,这意味着尾随的非单词不会使解析器变得疯狂。

{-# LANGUAGE OverloadedStrings #-}

module Atto where

--import qualified Data.Text.IO as Txt
import Data.Attoparsec.Text
import Control.Applicative ((*>), (<$>), (<|>), pure)

import qualified Data.Text as T

data Prose = Prose {
  word :: String
} deriving Show

optional :: Parser a -> Parser ()
optional p = option () (try p *> pure ())

-- Modified to disallow empty words, switched to applicative style
prose :: Parser Prose
prose = Prose <$> many1' letter

separator :: Parser ()
separator = many1 (space <|> satisfy (inClass ",.'")) >> pure ()

wordParser :: String -> [Prose]
wordParser str = case parseOnly wp (T.pack str) of
    Left err -> error err
    Right x -> x
    where
        wp = optional separator *> prose `sepBy1` separator

main :: IO ()
main = do
  let input = "Hello World, I am Elliot Anderson. \nAnd I'm Mr.Robot.\n"
  let words = wordParser input
  print words
  print $ length words

提供的解析器不会给出完全相同的结果,concatMap words . lines因为它也会中断.,'. 修改此行为只是一个简单的练习。

希望能帮助到你!:)


推荐阅读