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