我想读取文件,处理它,并将结果写入另一个文件;输入文件名将通过控制台参数提供,输出文件名由输入文件名生成。
如果没有提供参数,我希望它能透明地“故障转移”到stdin / stdout;实质上,如果提供了文件名,我将stdin / stdout重定向到相应的文件名,这样我就可以透明地使用 interact 是否提供了文件名。
这里的代码与多余的 else 中的虚拟输出一起被黑了。这样做的恰当的,惯用的形式是什么?
这可能与Control.Monad的 或后卫有关,正如在类似的问题中指出的那样,但也许有人写了这个已经
import System.IO
import Data.Char(toUpper)
import System.Environment
import GHC.IO.Handle
main :: IO ()
main = do
args <- getArgs
if(not $ null args) then
do
print $ "working with "++ (head args)
finHandle <- openFile (head args) ReadMode --open the supplied input file
hDuplicateTo finHandle stdin --bind stdin to finName's handle
foutHandle <- openFile ((head args) ++ ".out") WriteMode --open the output file for writing
hDuplicateTo foutHandle stdout --bind stdout to the outgoing file
else print "working through stdin/redirect" --get to know
interact ((++) "Here you go---\n" . map toUpper)
答案 0 :(得分:1)
这对我来说似乎相当惯用。我有一个注意事项是避免head
,因为它是一个不安全的函数(它可能会引发运行时错误)。在这种情况下,使用case
进行模式匹配非常容易。
main :: IO ()
main = do
args <- getArgs
case args of
fname:_ -> do
print $ "working with " ++ fname
finHandle <- openFile fname ReadMode
hDuplicateTo finHandle stdin
foutHandle <- openFile (fname ++ ".out") WriteMode
hDuplicateTo foutHandle stdout
[] -> do
print "working through stdin/redirect"
interact ((++) "Here you go---\n" . map toUpper)
答案 1 :(得分:1)
interact
没有什么特别之处 - 这是它的定义:
interact :: (String -> String) -> IO ()
interact f = do s <- getContents
putStr (f s)
这样的事情怎么样:
import System.Environment
import Data.Char
main = do
args <- getArgs
let (reader, writer) =
case args of
[] -> (getContents, putStr)
(path : _) -> let outpath = path ++ ".output"
in (readFile path, writeFile outpath)
contents <- reader
writer (process contents)
process :: String -> String
process = (++) "Here you go---\n" . map toUpper
根据命令行参数,我们将reader
和writer
设置为IO-actions,它将读取输入并写入输出。