ちょっと前に作った16進ダンププログラムをリファクタリングしてみました

id:ha-tan:20060802:1154448075で作った16進ダンププログラムをリファクタリングしてみました。リファクタリングのポイントはこんな感じ。

  • IOを分離する。
  • できるだけ自分で再帰を書かずに高階関数を利用する。

ファイル: hd.hs

module Main (main) where

import System (getArgs)
import System.IO (hSetBinaryMode, hGetContents, openBinaryFile, 
                  IOMode (ReadMode), stdin)
import Data.Char (ord, isAscii, isPrint)
import Text.Printf (printf)

readBinaryFiles :: [FilePath] -> IO String
readBinaryFiles [] = do
  hSetBinaryMode stdin True 
  getContents
readBinaryFiles files =
  return . concat =<< mapM f files
  where
    f file = hGetContents =<< openBinaryFile file ReadMode
  
groupn :: Int -> [a] -> [[a]]
groupn _ [] = []
groupn n xs =
  let (xs1, xs2) = splitAt n xs
  in xs1 : groupn n xs2

hexdump :: String -> [String]
hexdump = zipWith (\ o l -> off o ++ hex l ++ asc l) [0, 16 ..] . groupn 16
  where
    off :: Int -> String
    off = printf "%08x  "

    hex :: String -> String
    hex = pad 36 . concatMap f . groupn 4
      where
        f xs = (concatMap (printf "%02x") $ map ord xs) ++ " "

        pad n s = s ++ replicate (n - length s) ' '

    asc :: String -> String
    asc = (++) " " . map (\ c -> if isAscii c && isPrint c then c else '.')

main :: IO ()
main = mapM_ putStrLn . hexdump =<< readBinaryFiles =<< getArgs

実行例:

$ echo hoge | ./hd.exe
00000000  686f6765 0a                          hoge.