どうも、靖宗です。
お次はIO and the file system
ということで、ファイルIOなどでしょうか。
- The IO module(IOモジュール)
- The File module(Fileモジュール)
- The Path module(Pathモジュール)
- Processes and group leaders
- iodata and chardata
The IO module(IOモジュール)
IOモジュールはElixirの基本的なインプット/アウトプットの関数群のようです。
そういえばIO.puts/1
とか既に出てきてますよね。
iex(7)> IO.puts "hello world" hello world :ok iex(8)> IO.gets "yes or no? " yes or no? yes "yes\n"
pythonでいうprint()
とinput()
ですね。ここはまあ大丈夫。
IOモジュールは特に明示しない限りは標準入出力を使用するようですが、:stderr
など指定すれば標準エラー出力への出力もできるようです。
あまりWindowsでは意識したことありませんが・・・
iex> IO.puts :stderr, "hello world" hello world :ok
The File module(Fileモジュール)
Fileモジュールを利用すればファイルシステムを利用することができます。
これはどっかで既に出てきてた気がします。
iex> {:ok, file} = File.open "hello", [:write] {:ok, #PID<0.47.0>} iex> IO.binwrite file, "world" :ok iex> File.close file :ok iex> File.read "hello" {:ok, "world"}
書き込み用にファイルを開く場合はFile.open ファイル名 [:write]
みたいな感じでしょうか。
書き込み終了後に読み取りはいちいち開かなくて良さそうです。
File.open
の返値にプロセスIDが返ってきてる所をみると、File.open
もStoreの様に別プロセスで値を保持してるかんじでしょうか。
あと、UNIXのコマンドっぽい関数も実装されているようです。これは適宜調べれば良さそう。
File.read
などは2種類関数が用意されているようで、File.read!
もあるみたいです。
一般的に{:ok, "hogehoge"}
のようにタプルで返ってくるのが、中身だけ返ってくる関数のようです。
iex> File.read "hello" {:ok, "world"} iex> File.read! "hello" "world" iex> File.read "unknown" {:error, :enoent} iex> File.read! "unknown" ** (File.Error) could not read file "unknown": no such file or directory
ファイルが開けない場合はエラーがでると。
!
無し版ならcase
でパターンマッチの条件分岐が書ける。
case File.read(file) do {:ok, body} -> # do something with the `body` {:error, reason} -> # handle the error caused by `reason` end
でもパターンマッチでエラーが起こるので十分って人はパターンマッチだけ書けば良さそう。
{:ok, body} = File.read(file)
いずれにしろ!
無しの方がよさそう。
The Path module(Pathモジュール)
iex> Path.join("foo", "bar") "foo/bar" iex> Path.expand("~/hello") "/Users/jose/hello"
のようです。
コレがあるのと無いのとでは多種のOSで実行するときに結構違うので助かります。
Processes and group leaders
このセクションスキップして良いよって書かれてたけど、そうすると永遠に見ない気がするので一応みときます。
File.open
でプロセスIDが返ってきてたのはやはり別プロセスで実行されているようで、IO.write(pid, binary)
でメッセージを送る感じで書き込み可能?
iex(14)> pid = spawn fn -> ...(14)> receive do: (msg -> IO.inspect msg) ...(14)> end #PID<0.124.0> iex(15)> IO.write(pid, "hello") {:io_request, #PID<0.102.0>, #Reference<0.1171378494.2642411524.2038>, {:put_chars, :unicode, "hello"}} ** (ErlangError) Erlang error: :terminated (stdlib) :io.put_chars(#PID<0.124.0>, :unicode, "hello")
IO.write
などでメッセージングが規格化されているイメージでしょうか。
おそらく普段使いでは気にしなくていい気がします。
StringIO
というモジュールに関して。
なんか使いどころが分かりませんが文字列もIOデバイスとして取り扱える?
iex> {:ok, pid} = StringIO.open("hello") {:ok, #PID<0.43.0>} iex> IO.read(pid, 2) "he"
group leader
という特殊なIOデバイスが存在しているそうで、そいつに向けてメッセージを送信することもできる。
エラー処理とかに使う?
iex> IO.puts :stdio, "hello" hello :ok iex> IO.puts Process.group_leader, "hello" hello :ok
標準入出力がgroup leader
らしいけど調整できるっぽい。
使いどころがイマイチわからないので時が来たらまた考えましょう。
iodata and chardata
今までの例ではバイナリ文字列を使ってましたが、charlistsや文字コードのリストでも行けるよ!って話。たぶん。
iex> IO.puts 'hello world' hello world :ok iex> IO.puts ['hello', ?\s, "world"] hello world :ok
たぶんこういうバイナリ文字列やcharlists、文字列コードリストのことを総称してiodata
とよんでおり(?)、iodata
が引数として考えられている。
標準出力やファイルを開くときにエンコードを指定している場合はchar_data
という文字列などが引数として期待されている。
うーん、なんとなく分かるけど実感としてイマイチピンとこない。
おそらくバイナリデータとかを扱わないといまいちな箇所かも。
詰まったら勉強し直そう。