文件操作 #

一、基本文件操作 #

1.1 读取文件 #

haskell
-- readFile:读取整个文件(惰性)
readFile :: FilePath -> IO String

-- 示例
main :: IO ()
main = do
    contents <- readFile "input.txt"
    putStrLn contents

1.2 写入文件 #

haskell
-- writeFile:写入文件(覆盖)
writeFile :: FilePath -> String -> IO ()

-- 示例
main :: IO ()
main = do
    writeFile "output.txt" "Hello, World!"
    putStrLn "File written"

1.3 追加文件 #

haskell
-- appendFile:追加到文件
appendFile :: FilePath -> String -> IO ()

-- 示例
main :: IO ()
main = do
    appendFile "log.txt" "New log entry\n"
    putStrLn "Log appended"

二、文件句柄 #

2.1 打开文件 #

haskell
import System.IO

-- 打开文件
openFile :: FilePath -> IOMode -> IO Handle

-- IOMode
data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode

-- 示例
main :: IO ()
main = do
    handle <- openFile "input.txt" ReadMode
    contents <- hGetContents handle
    putStrLn contents
    hClose handle

2.2 句柄操作 #

haskell
import System.IO

main :: IO ()
main = do
    handle <- openFile "input.txt" ReadMode
    
    -- 读取操作
    line <- hGetLine handle
    contents <- hGetContents handle
    
    -- 写入操作
    hPutStrLn handle "text"
    hPutStr handle "text"
    
    hClose handle

2.3 withFile #

haskell
import System.IO

-- withFile:自动关闭文件
withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a

-- 示例
main :: IO ()
main = do
    contents <- withFile "input.txt" ReadMode $ \handle -> do
        hGetContents handle
    putStrLn contents

三、目录操作 #

3.1 目录操作 #

haskell
import System.Directory

-- 获取当前目录
getCurrentDirectory :: IO FilePath

-- 设置当前目录
setCurrentDirectory :: FilePath -> IO ()

-- 获取目录内容
getDirectoryContents :: FilePath -> IO [FilePath]

-- 创建目录
createDirectory :: FilePath -> IO ()

-- 删除目录
removeDirectory :: FilePath -> IO ()

-- 示例
main :: IO ()
main = do
    cwd <- getCurrentDirectory
    putStrLn $ "Current directory: " ++ cwd
    
    createDirectory "test_dir"
    contents <- getDirectoryContents "."
    print contents
    
    removeDirectory "test_dir"

3.2 文件检查 #

haskell
import System.Directory

-- 检查文件是否存在
doesFileExist :: FilePath -> IO Bool

-- 检查目录是否存在
doesDirectoryExist :: FilePath -> IO Bool

-- 示例
main :: IO ()
main = do
    fileExists <- doesFileExist "input.txt"
    if fileExists
        then putStrLn "File exists"
        else putStrLn "File not found"
    
    dirExists <- doesDirectoryExist "src"
    putStrLn $ "Directory exists: " ++ show dirExists

3.3 文件操作 #

haskell
import System.Directory

-- 复制文件
copyFile :: FilePath -> FilePath -> IO ()

-- 移动/重命名文件
renameFile :: FilePath -> FilePath -> IO ()

-- 删除文件
removeFile :: FilePath -> IO ()

-- 示例
main :: IO ()
main = do
    copyFile "input.txt" "input_backup.txt"
    renameFile "input.txt" "renamed.txt"
    removeFile "renamed.txt"

四、异常处理 #

4.1 基本异常处理 #

haskell
import Control.Exception

-- catch:捕获异常
catch :: IO a -> (SomeException -> IO a) -> IO a

-- 示例
main :: IO ()
main = do
    result <- catch (readFile "nonexistent.txt") handleError
    putStrLn result

handleError :: SomeException -> IO String
handleError e = return $ "Error: " ++ show e

4.2 特定异常类型 #

haskell
import Control.Exception
import System.IO.Error

-- 捕获IO错误
catchIOError :: IO a -> (IOError -> IO a) -> IO a

-- 示例
main :: IO ()
main = do
    result <- catchIOError (readFile "nonexistent.txt") handleIOError
    putStrLn result

handleIOError :: IOError -> IO String
handleIOError e
    | isDoesNotExistError e = return "File not found"
    | isPermissionError e   = return "Permission denied"
    | otherwise             = return $ "Error: " ++ show e

4.3 try #

haskell
import Control.Exception

-- try:返回Either
try :: IO a -> IO (Either SomeException a)

-- 示例
main :: IO ()
main = do
    result <- try (readFile "nonexistent.txt") :: IO (Either SomeException String)
    case result of
        Left e  -> putStrLn $ "Error: " ++ show e
        Right s -> putStrLn s

4.4 bracket #

haskell
import Control.Exception

-- bracket:确保资源释放
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c

-- 示例
main :: IO ()
main = do
    contents <- bracket 
        (openFile "input.txt" ReadMode)  -- 获取资源
        hClose                            -- 释放资源
        hGetContents                      -- 使用资源
    putStrLn contents

五、文件信息 #

5.1 获取文件大小 #

haskell
import System.Directory

-- 获取文件大小
getFileSize :: FilePath -> IO Integer

-- 示例
main :: IO ()
main = do
    size <- getFileSize "input.txt"
    putStrLn $ "File size: " ++ show size ++ " bytes"

5.2 文件时间 #

haskell
import System.Directory

-- 获取修改时间
getModificationTime :: FilePath -> IO UTCTime

-- 示例
main :: IO ()
main = do
    time <- getModificationTime "input.txt"
    putStrLn $ "Last modified: " ++ show time

六、实践示例 #

6.1 文件复制程序 #

haskell
import System.Directory

main :: IO ()
main = do
    putStrLn "Enter source file:"
    src <- getLine
    exists <- doesFileExist src
    if not exists
        then putStrLn "Source file not found"
        else do
            putStrLn "Enter destination file:"
            dst <- getLine
            copyFile src dst
            putStrLn "File copied successfully!"

6.2 文件搜索 #

haskell
import System.Directory
import System.FilePath

searchFiles :: FilePath -> String -> IO [FilePath]
searchFiles dir pattern = do
    exists <- doesDirectoryExist dir
    if not exists
        then return []
        else do
            entries <- getDirectoryContents dir
            let files = filter (not . isSpecial) entries
            results <- mapM (checkEntry dir pattern) files
            return (concat results)
  where
    isSpecial "." = True
    isSpecial ".." = True
    isSpecial _ = False

checkEntry :: FilePath -> String -> FilePath -> IO [FilePath]
checkEntry dir pattern entry = do
    let path = dir </> entry
    isDir <- doesDirectoryExist path
    if isDir
        then searchFiles path pattern
        else if match pattern entry
            then return [path]
            else return []

match :: String -> String -> Bool
match pattern entry = pattern `elem` entry

6.3 日志文件 #

haskell
import System.Directory
import Data.Time

logFile :: FilePath
logFile = "app.log"

logMessage :: String -> IO ()
logMessage msg = do
    time <- getCurrentTime
    let entry = show time ++ " - " ++ msg ++ "\n"
    appendFile logFile entry

main :: IO ()
main = do
    logMessage "Application started"
    logMessage "Processing data..."
    logMessage "Application finished"
    
    putStrLn "Log contents:"
    contents <- readFile logFile
    putStrLn contents

6.4 配置文件 #

haskell
import System.Directory
import Data.Maybe

type Config = [(String, String)]

readConfig :: FilePath -> IO Config
readConfig path = do
    exists <- doesFileExist path
    if not exists
        then return []
        else do
            contents <- readFile path
            return $ parseConfig contents

parseConfig :: String -> Config
parseConfig = map parseLine . filter (not . null) . lines
  where
    parseLine line =
        let (key, value) = span (/= '=') line
        in (trim key, trim (drop 1 value))
    
    trim = dropWhile (== ' ') . reverse . dropWhile (== ' ') . reverse

getConfig :: String -> Config -> Maybe String
getConfig key = lookup key

main :: IO ()
main = do
    config <- readConfig "config.txt"
    case getConfig "host" config of
        Just host -> putStrLn $ "Host: " ++ host
        Nothing   -> putStrLn "Host not configured"

七、总结 #

文件操作要点:

  1. 基本操作readFilewriteFileappendFile
  2. 文件句柄openFilehGetLinehClose
  3. 目录操作getDirectoryContentscreateDirectory
  4. 文件检查doesFileExistdoesDirectoryExist
  5. 异常处理catchtrybracket
  6. 资源管理:使用withFile确保资源释放

掌握文件操作后,让我们继续学习高级特性。

最后更新:2026-03-27