我是Haskell的新手,我刚刚开始学习如何使用attoparsec从.txt文件中解析大块的英文文本。我知道如何在不使用attoparsec的情况下获取.txt文件中的单词数量,但我有点坚持使用attoparsec。当我在下面运行我的代码时,让我们说
“Hello World,我是Elliot Anderson。\ n我是Mr.Robot。\ n”
我才回来:
世界,我是艾略特安德森。 \ n我是Mr.Robot。\ n“(散文{word = “你好”})
这是我目前的代码:
{-# 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的任何建议?
答案 0 :(得分:3)
你已经有一个很好的开始 - 你可以解析一个词
您接下来需要的是Parser [Prose]
,可以通过使用prose
或sepBy
将sepBy1
解析器与使用“非散文”部分的另一个解析器相结合来表达,您可以在Data.Attoparsec.Text
文档中查找。
从那里,获得单词计数的最简单方法是简单地获得所获得的[Prose]
的长度。
编辑:
这是一个最小的工作示例。已将Parser
转轮换成parseOnly
以允许忽略剩余输入,这意味着尾随的非单词不会使解析器变为cray-cray。
{-# 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
的结果不完全相同,因为它还会破坏.,'
上的单词。修改此行为只是一个简单的练习。
希望它有所帮助! :)
答案 1 :(得分:2)
你走在正确的轨道上!您编写了一个解析器(prose
),它只读取一个单词:many' letter
识别一系列字母。
现在您已经想出如何解析单个单词,您的工作就是扩展它以解析由空格分隔的单词序列。这就是sepBy
的作用:p `sepBy` q
重复运行p
解析器并散布q
解析器。
因此,一系列单词的解析器看起来像这样(我冒昧地将prose
重命名为word
):
word = many letter
phrase = word `sepBy` some space -- "some" runs a parser one-or-more times
ghci> parseOnly phrase "wibble wobble wubble" -- with -XOverloadedStrings
Right ["wibble","wobble","wubble"]
现在,由phrase
和letter
组成的space
将会死在非字母非空格字符上,例如'
和.
。我会留给你弄清楚如何解决这个问题。 (作为提示,您可能需要将many letter
更改为many (letter <|> ...)
,具体取决于您希望它在各种标点符号上的行为方式。)