Files and IO
| Read a file | |
| Write a file | |
| lines and unlines | |
| Handles and withFile |
Read a file
readFile returns the whole file as one string inside IO. It is lazy: the file is opened and read as the string is consumed, which is convenient and occasionally surprising when the file changes underneath:
main :: IO ()
main = do
text <- readFile "input.txt"
putStrLn ("bytes: " ++ show (length text))
A missing file raises an IOException at runtime. Lesson 46 shows how to catch it; for now, keep the filename in a variable so the error message is easy to reproduce.
Write a file
writeFile replaces the file and appendFile adds to the end. Both take the content as a plain string, so the pure part of the program builds it first:
main = do
writeFile "out.txt" "first line\n"
appendFile "out.txt" "second line\n"
The newline is explicit. Nothing appends it for you, which is why the helper functions in the next section exist.
lines and unlines
Two functions convert between a string and a list of lines. lines splits on newlines and drops the terminators, and unlines puts them back:
lines "a\nb\n" -- ["a","b"]
unlines ["a","b"] -- "a\nb\n"
-- number every line of a file
numbered text = unlines
[ show n ++ ": " ++ line
| (n, line) <- zip [1..] (lines text) ]
numbered is pure, so it can be tested with a string literal and no file at all. That is the shell pattern from lesson 33 applied to text processing.
Handles and withFile
For a file read in several passes, open a handle and close it with a bracketed helper. withFile closes the handle even if the body throws:
import System.IO
main = withFile "out.txt" WriteMode \h -> do
hPutStrLn h "written through a handle"
The manual version calls openFile and
hClose with no guarantee about the second one.
Prefer the bracketed form, and use
Data.Text.IO instead of
String once files grow past a few megabytes.
Next:
Modules and imports
Article author: Arthur Isaev