我正在尝试用 Haskell 编写一个简单的 cat
程序。我想采用多个文件名作为参数,并将每个文件按顺序写入 STDOUT,但我的程序只打印一个文件并退出。
我需要做什么才能让我的代码打印每个文件,而不仅仅是传入的第一个文件?
import Control.Monad as Monad
import System.Exit
import System.IO as IO
import System.Environment as Env
main :: IO ()
main = do
-- Get the command line arguments
args <- Env.getArgs
-- If we have arguments, read them as files and output them
if (length args > 0) then catFileArray args
-- Otherwise, output stdin to stdout
else catHandle stdin
catFileArray :: [FilePath] -> IO ()
catFileArray files = do
putStrLn $ "==> Number of files: " ++ (show $ length files)
-- run `catFile` for each file passed in
Monad.forM_ files catFile
catFile :: FilePath -> IO ()
catFile f = do
putStrLn ("==> " ++ f)
handle <- openFile f ReadMode
catHandle handle
catHandle :: Handle -> IO ()
catHandle h = Monad.forever $ do
eof <- IO.hIsEOF h
if eof then do
hClose h
exitWith ExitSuccess
else
hGetLine h >>= putStrLn
我正在运行这样的代码:
runghc cat.hs file1 file2
最佳答案
您的问题是 exitWith
终止整个程序。因此,您不能真正使用 forever
来循环遍历文件,因为显然您不想“永远”运行该函数,直到文件末尾。你可以像这样重写catHandle
catHandle :: Handle -> IO ()
catHandle h = do
eof <- IO.hIsEOF h
if eof then do
hClose h
else
hGetLine h >>= putStrLn
catHandle h
即如果还没有到达 EOF,我们就会递归并读取另一行。
但是,整个方法过于复杂。您可以将 cat 简单地写为
main = do
files <- getArgs
forM_ files $ \filename -> do
contents <- readFile filename
putStr contents
由于惰性 I/O,整个文件内容实际上并未加载到内存中,而是流入 stdout。
如果您对 Control.Monad
中的运算符感到满意,整个程序可以缩短为
main = getArgs >>= mapM_ (readFile >=> putStr)
关于haskell - 如何在 Haskell 中实现 `cat` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11475006/