HaskellのファイルIO

(2009.5.12) ページを分割し、加筆。

Haskellでファイルを読み書きしたりする方法について。

readFile, writeFile

簡単にファイルを読み込むには、Prelude モジュールの readFile 関数を呼び出します。型はこう;

Haskell
[POPUP]
  1. readFile :: FilePath -> IO String
  2. type FilePath = String

また、ファイルに書き込むには、writeFile 関数を呼び出します。

Haskell
[POPUP]
  1. writeFile :: FilePath -> String -> IO ()

readFileの第一引数はファイル名で、ファイルを開くところからしてくれます。実際の読み込みは遅延評価され、必要でない部分は読み込まれません。

次の例は単にファイルの内容を表示します。

Haskell
[POPUP]
  1. main = do
  2. cs <- readFile "03readfile.hs"
  3. putStr cs

次の例はファイルの先頭5行のみを表示します。必要でない部分は読み込まれません。

Haskell
[POPUP]
  1. doHead :: Int -> String -> String
  2. doHead n cs =
  3. unlines $ take n $ lines cs
  4. main = do
  5. cs <- readFile "maybe.hs"
  6. putStr $ doHead 5 cs

例外の捕捉

ファイルが見つからないときは実行時エラー (例外) が投げられます。例外はcatch関数で捕まえます。

catch関数の型は、

Haskell
[POPUP]
  1. catch :: IO a -> (IOError -> IO a) -> IO a

catchの第一引数が例外が発生するかもしれない処理、第2引数がエラーハンドラ関数です。

例外が発生したときは第2引数の関数が呼び出されて、catchの戻り値はその関数の戻り値になります。

次の例は、ファイルが見つからなかったらエラーメッセージを表示します。

Haskell
[POPUP]
  1. import System
  2. import IO -- hPutStrLn
  3. onError :: String -> IOError -> IO String -- (1)
  4. onError filename error = do
  5. hPutStrLn stderr $ "File not found: " ++ filename
  6. return []
  7. main = do
  8. args <- getArgs -- (2)
  9. content <- if null args -- null はリストが空なら真を返す
  10. then getContents
  11. else catch (readFile $ head args)
  12. (onError $ head args)
  13. putStr content

(1) catchの第2引数として渡すためにonErrorを定義します。戻り値の型は、catchの第1引数に合わせます。readFileがIO Stringなので、IO Stringです。

(2) getArgs関数はコマンドライン引数の配列を返します。

コマンドの引数が与えられていないときは標準入力から読み込み、引数があるときはそのファイルを読み込みます。

この例は作為的で、普通ならputStrも例外が発生するかもしれない処理のなかに書くところです。

ファイルハンドル

readFile, writeFile は大雑把すぎて、書き込み・読み込みモードを指定したりできません。

自分で制御したいときは System.IO モジュールの関数を用います。名前からだいたい何をするものか見当がつきます。

Haskell
[POPUP]
  1. openFile :: FilePath -> IOMode -> IO Handle
  2. data IOMode
  3. = ReadMode
  4. | WriteMode
  5. | AppendMode
  6. | ReadWriteMode
  7. hClose :: Handle -> IO ()
  8. hGetChar :: Handle -> IO Char
  9. hGetLine :: Handle -> IO String
  10. hPutChar :: Handle -> Char -> IO ()
  11. hPutStr :: Handle -> String -> IO ()
  12. hPutStrLn :: Handle -> String -> IO ()

TODO: 説明を追加。