wcコマンドを実装してみました。
標準入力からの読み込みと(いい加減な)桁合わせは実装しました。Windowsはデフォルトでテキストモードでファイルをオープンするので(このようなバイト単位で数えないといけない場面では)面倒です。
module Main (main) where import System.Environment (getArgs) import System.IO (stdin, openBinaryFile, hGetContents, hSetBinaryMode, IOMode(ReadMode)) import Text.Printf (printf) readBinaryFile :: FilePath -> IO String readBinaryFile path = hGetContents =<< openBinaryFile path ReadMode getBinaryContents :: IO String getBinaryContents = do hSetBinaryMode stdin True getContents data WC = WC { wcBytes :: Int, wcWords :: Int, wcLines :: Int, wcPath :: FilePath } wcCount :: FilePath -> IO WC wcCount path = wcCount' path =<< readBinaryFile path wcCountFromStdin :: IO WC wcCountFromStdin = wcCount' "" =<< getBinaryContents wcCount' path s = do let b = length s w = length $ words s l = length $ lines s return $ WC b w l path wcFormat :: Int -> WC -> String wcFormat len wc = let s = show len fmt = "%" ++ s ++ "d %" ++ s ++ "d %" ++ s ++ "d %s\n" in printf fmt (wcLines wc) (wcWords wc) (wcBytes wc) (wcPath wc) wcTotal :: [WC] -> WC wcTotal ws = let b = sum $ map wcBytes ws w = sum $ map wcWords ws l = sum $ map wcLines ws in WC b w l "total" main :: IO () main = do args <- getArgs if (null args) then putStr . wcFormat 7 =<< wcCountFromStdin else do ws <- mapM wcCount args let ws' = if (length ws > 1) then ws ++ [wcTotal ws] else ws len = maximum $ map (length . show . wcBytes) ws' putStr $ concatMap (wcFormat len) ws' -- Local Variables: -- compile-command: "ghc -W -fno-warn-unused-matches --make -o wc wc" -- End:
BUGS:
- エラーが発生するとそこで終了します。エラーが発生したファイルをスキップする等の処理は実装していません。
- 出力は最後にまとめて行っているので、大量のファイルを与えるとなかなか出力されません。
- 出力の桁数を求めるアルゴリズムがGNUのwcとは違うみたいです(GNU wcのソース読んで調べてません)。
- コマンドラインオプションは実装していません。
実行例:
$ ./wc * 1825 14861 779820 wc.exe 5 14 364 wc.hi 61 267 1673 wc.hs 40 284 20148 wc.o 1931 15426 802005 total