ファイルを扱うための関数は、Fileモジュールで提供されています。
ファイル全体を読み取るには、File.read/1を使います。ファイルに書き込むには、File.write/2を使います。
File.write/2でファイルに書き込むたびに、ファイルディスクリプターが開かれ、新しいElixirのプロセスが生成されます。そのため、File.write/2を使ってループの中でファイルに書き込むのは避けましょう。
代わりに、File.open/2を使ってファイルを開くことができます。File.open/2の2番目の引数はモードのリストで、ファイルを読み取り用に開くのか、書き込み用に開くのかを指定できます。
File.open/2は、そのファイルを扱うプロセスのPIDを返します。ファイルの読み取りと書き込みを行うには、IOモジュールの関数を使い、このPIDをIOデバイスとして渡します。
ファイルの操作が終わったら、File.close/1で閉じます。
ここまでに挙げたFileモジュールの関数には、エラータプルを返す代わりにエラーを発生させる!付きの変種もあります(例:File.read!/1)。ファイルが存在しない、権限がないといったエラーを処理するつもりがない場合は、こちらの変種を使いましょう。
鉄道模型が大好きで、その情熱を世界と共有しようと、ニュースレターを始めることにしました。まずは、同じ趣味を持つ友人や知人に、ニュースレターの第1号を送るところから始めます。手元には、彼らのメールアドレスの一覧が書かれたテキストファイルがあります。
Newsletter.read_emails/1関数を実装しましょう。ファイルパスを1つ受け取ります。ファイルは、メールアドレスが改行で区切られて並んでいるテキストファイルです。関数は、ファイルから読み込んだメールアドレスの配列を返します。
Newsletter.read_emails("/home/my_user/documents/model_train_friends_emails.txt")
# => ["rick@example.com", "choochoo42@example.com", "anna@example.com"]
メールの送信は、メールアドレスの打ち間違いや一時的なネットワークの問題など、予測できないさまざまな理由で失敗する可能性のある作業です。重複して送ることなく、友人全員へのメール送信をやり直せるようにするには、すでにメールを送ったアドレスを記録しておく必要があります。そのためにログファイルを使います。
Newsletter.open_log/1関数を実装しましょう。ファイルパスを1つ受け取り、書き込み用にファイルを開き、そのファイルを扱うプロセスのPIDを返します。
Newsletter.open_log("/home/my_user/documents/newsletter_issue1_log.txt")
# => #PID<0.145.0>
Newsletter.log_sent_email/2関数を実装しましょう。ファイルを扱うプロセスのPIDと、メールアドレスが入った文字列を受け取ります。ファイルにメールアドレスを書き込み、その後に改行を加えます。
Newsletter.log_sent_email(pid, "joe@example.com")
# => :ok
Newsletter.close_log/1関数を実装しましょう。ファイルを扱うプロセスのPIDを受け取り、ファイルを閉じます。
Newsletter.close_log(pid)
# => :ok
メール送信の手順を組み立てる部品がそろったので、それらを1つの関数にまとめます。
Newsletter.send_newsletter/3関数を実装しましょう。メールアドレスが書かれたファイルのパス、ログファイルのパス、そして指定されたメールアドレスにメールを送る無名関数を受け取ります。指定されたファイルからすべてのメールアドレスを読み込み、そのすべてにメールを送ろうとします。メールを送る無名関数が:okを返したら、そのメールアドレスをログファイルに書き込み、その後に改行を加えます。メールを送ったらすぐに行うようにしましょう。すべての送信が終わったら、ログファイルを閉じます。
Newsletter.send_newsletter(
"model_train_friends_emails.txt",
"newsletter_issue1_log.txt",
fn email -> :ok end
)
# => :ok