Haskell에서 파일을 다루는 방법은 다른 언어와 크게 다르지 않다. Unix에서 파일의 단어 개수를 세 주는 wc라는 유틸리티를 만들어보자.
module Main where
import System
main :: IO ()
main = do
arg <- getArgs
filename <- return ( head arg )
contents <- readFile filename
wc <- return ( length ( words contents ))
print wc
getArgs는 프로그램의 매개변수를 읽어서 리스트로 전달해준다. 즉, "wc -l test.txt"라고 입력했다면 arg는 ["-l", "test.txt"]가 될 것이다.
Haskell 기본 라이브러리인 Prelude에는 파일을 다루는 함수가 세 가지 있는데 readFile, writeFile, appendFile이다. 이름 그대로 readFile은 파일을 읽어 문자열로 돌려준다. writeFile과 appendFile은 문자열을 파일에 기록하는 데 writeFile은 항상 새 파일을 만들고, appendFile은 기존 파일의 끝에 이어서 기록한다는 차이가 있다. 이들을 이용해 이번에는 복사 프로그램을 만들어보자.
module Main where
import IO
import System
main :: IO ()
main = do
arg <- getArgs
let
source = arg !! 0
target = arg !! 1
readFile source >>= writeFile target
원래는 source <- return ( arg !! 0 )이라고 써야하는 부분에 let source = arg !! 0이라고 썼다. syntactic sugar로 도입된 것인데 어느 쪽이든 편한 방식으로 쓰면 된다. 파일을 좀 더 정교하게 다루려면 IO 모듈에 있는 함수들을 사용해야 한다.
module Main where
import IO
import System
main :: IO ()
main = do
arg <- getArgs
let
source = arg !! 0
target = arg !! 1
-- 파일 열기
from <- openFile source ReadMode
to <- openFile target WriteMode
-- 복사
contents <- hGetContents from
hPutStr to contents
-- 파일 닫기
hClose from
hClose to
openFile은 파일을 열어서 핸들을 돌려준다. 파일 입출력은 이 핸들을 통해 이뤄진다. 파일을 모두 사용하면 hClose로 파일을 닫고 핸들을 운영체제에 돌려준다.
파일을 여는 옵션은 네 가지가 있는 데 ReadMode, WriteMode, AppendMode, ReadWriteMode이다. hGetContents는 핸들의 내용 전부를 읽어들이는 함수다. 그 외에도 핸들을 다루는 함수에는 문자 하나 단위로 입출력 하는 hGetChar와 hPutChar, 줄 단위로 읽는 hGetLine과 문자열을 출력하는 hPutStr 등이 있다.
그런데 이렇게 만든 복사 프로그램이 MS윈도에서는 텍스트 문서만 복사할 수 있다. Unix와 텍스트 형식이 다르기 때문에 Haskell은 파일을 읽고 쓸 때 자동으로 이 형식을 맞춰주는 데 실행 파일을 복사할 경우 오히려 파일 내용을 바꿔버리게 된다. 이런 자동 변환없이 파일을 읽고 쓰려면 바이너리 모드로 사용해야 한다. Haskell 표준에는 바이너리 파일에 대한 정의가 없지만 GHC에서는 지원한다. 간단히 openFile 대신 openBinaryFile 함수를 쓰면 된다. 그리고 IO대신 System.IO 모듈을 불러들인다(import).
module Main where
import System.IO
import System.Environment
main :: IO ()
main = do
arg <- getArgs
let
source = arg !! 0
target = arg !! 1
-- 파일 열기
from <- openBinaryFile source ReadMode
to <- openBinaryFile target WriteMode
-- 복사
contents <- hGetContents from
hPutStr to contents
-- 파일 닫기
hClose from
hClose to