ファイルを扱うための関数は、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の第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を使って1行ずつ読み込むか、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"