파일을 다루는 함수는 File 모듈에서 제공해요.
파일 전체를 읽으려면 File.read/1을 사용해요. 파일에 쓰려면 File.write/2를 사용해요.
이 모듈은 복사, 삭제, 이름 바꾸기 등의 파일 함수도 제공해요. 이름은 UNIX의 대응 명령어와 비슷해요. 예를 들면 다음과 같아요:
File.cp/3 - 파일을 복사해요.File.rm/1 - 파일을 삭제해요.File.rename/2 - 파일의 이름을 바꾸거나 이동해요.File.mkdir/1 - 디렉터리를 만들어요.File.cwd/0 - 현재 작업 디렉터리를 가져와요.File 모듈의 위에서 언급한 모든 함수에는 오류 튜플을 반환하는 대신 오류를 발생시키는 ! 변형이 있어요(예: File.read!/1). 파일이 없거나 권한이 없는 등의 오류를 처리할 생각이 없다면 이 변형을 사용해요.
File.read("does_not_exist")
# => {:error, :enoent}
File.read!("does_not_exist")
# => ** (File.Error) could not read file "does_not_exist": no such file or directory
# (elixir 1.10.4) lib/file.ex:353: File.read!/1
File.write/2로 파일에 쓸 때마다 파일 디스크립터가 열리고 새로운 Elixir 프로세스가 생성돼요. 이런 이유로 File.write/2를 사용해 루프 안에서 파일에 쓰는 것은 피해야 해요.
대신 File.open/2로 파일을 열 수 있어요. File.open/2의 두 번째 인자는 모드 목록으로, 파일을 읽기용으로 열지 쓰기용으로 열지 지정할 수 있어요.
자주 사용하는 모드는 다음과 같아요:
:read - 읽기용으로 열고, 파일이 반드시 있어야 해요:write - 쓰기용으로 열고, 파일이 없으면 만들어지며, 기존 내용은 덮어써져요:append - 쓰기용으로 열고, 파일이 없으면 만들어지며, 기존 내용은 그대로 유지돼요모든 모드의 전체 목록은 File.open/2 문서를 참고해요.
File.open/2는 파일을 다루는 프로세스의 PID를 반환해요. 파일을 읽고 쓰려면 IO 모듈의 함수를 사용하고, 이 PID를 IO 장치로 넘겨줘요.
파일 작업을 마치면 File.close/1로 파일을 닫아요.
file = File.open!("README.txt", [:write])
# => #PID<0.157.0>
IO.puts(file, "# README")
# => :ok
File.close(file)
# => :ok
File.read/1로 파일을 읽으면 파일 전체가 한 번에 메모리에 올라가요. 아주 큰 파일을 다룰 때는 문제가 될 수 있어요. 이런 파일을 효율적으로 다루려면 File.open/2와 IO.read/2로 파일을 한 줄씩 읽거나, File.stream/3으로 파일을 스트리밍할 수 있어요. 이 스트림은 Enumerable과 Collectable 프로토콜을 모두 구현하기 때문에 읽기와 쓰기에 모두 사용할 수 있어요.
File.stream!("file.txt")
|> Stream.map(&(&1 <> "!"))
|> Stream.into(File.stream!("new_file.txt"))
|> Stream.run()
파일을 다루는 모든 함수에는 파일 경로가 필요해요. 파일 경로는 절대 경로일 수도 있고 현재 디렉터리를 기준으로 한 상대 경로일 수도 있어요. 경로를 다루려면 Path 모듈의 함수를 사용해요. 여러 플랫폼에서 호환되게 하려면 Path.join/1로 경로를 만들어요. 이 함수는 플랫폼에 맞는 구분자를 골라줘요.
Path.expand(Path.join(["~", "documents", "important.txt"]))
"/home/user/documents/important.txt"