我对 Haskell 很陌生,目前正在尝试解决需要一些字符串解析的问题。我的输入字符串包含逗号分隔的引号中的单词列表。我想将此单个字符串解析为字符串的单词列表。我应该从哪里开始学习解析这样的字符串?是否有部分模块和/或功能会有所帮助?
ps 请不要发布完整的解决方案。我只是要求一个指向起点的指针,这样我就可以学习如何去做。
我最终决定推出自己的解析功能,因为这是一个如此简单的情况。自从我第一次发布这个问题并想在这里记录我的解决方案以来,我已经学到了很多关于 Haskell 的知识:
split :: Char -> String -> [String]
split _ "" = []
split c s = firstWord : (split c rest)
where firstWord = takeWhile (/=c) s
rest = drop (length firstWord + 1) s
removeChar :: Char -> String -> String
removeChar _ [] = []
removeChar ch (c:cs)
| c == ch = removeChar ch cs
| otherwise = c:(removeChar ch cs)
main = do
handle <- openFile "input/names.txt" ReadMode
contents <- hGetContents handle
let names = sort (map (removeChar '"') (split ',' contents))
print names
hClose handle
最强大的解决方案是解析器组合器。Haskell 有其中几个,但我想到的最重要的是:
do
解析器组合器的一大优点是使用符号(或Applicative
样式,如果您愿意)很容易定义解析器。
如果您只想要一些快速简单的字符串操作功能,请查阅text
库(对于高性能字节编码字符串)或Data.List
(对于普通列表编码字符串),它提供了操作字符串的必要功能。
由于String
s 只是Char
Haskell 中的 s 列表,因此 Data.List将是开始查找的好地方(为了学习 Haskell)。
对于更复杂的情况(例如,逗号可能嵌套在引号内并且应该被忽略),parsec(如 Daniel 提到的)将是一个更好的解决方案。
此外,如果您要解析 CSV,您可以尝试Text.CSV,尽管我没有尝试过,所以我不能说它会有多大帮助。
这是一种特别厚颜无耻的方法:
parseCommaSepQuotedWords :: String -> [String]
parseCommaSepQuotedWords s = read ("[" ++ s ++ "]")
这可能有效,但它非常脆弱而且相当愚蠢。本质上,您使用的是 Haskell 编写字符串列表的方式几乎与您的方式一致的事实,因此内置Read
实例几乎就是您想要的东西。您可以使用reads
更好的错误报告,但实际上您可能想要完全做其他事情。
总的来说,parsec
它真的值得一看——它使用起来很有趣,也是最初让我对 Haskell 感到兴奋的事情之一。但是如果你想要一个本土的解决方案,我经常使用关于 and 的结果的语句来编写简单的case
东西。假设您正在寻找输入中的下一个分号。然后将返回,其中:span
break
break (== ';') inp
(before, after)
before
是inp
最多(不包括)第一个分号的内容(如果没有,则为全部)after
是字符串的其余部分:
after
不为空,则第一个元素是分号before ++ after == inp
因此,要解析以分号分隔的语句列表,我可能会这样做:
parseStmts :: String -> Maybe [Stmt]
parseStmts inp = case break (== ';') inp of
(before, _ : after) -> -- ...
-- ^ before is the first statement
-- ^ ignore the semicolon
-- ^ after is the rest of the string
(_, []) -> -- inp doesn't contain any semicolons
为了给遇到这个问题的人一个完整的答案,Data.Text也有一些很好的功能。
将 parsec 用于任何“真正的工作”。