エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

Io言語が面白い・Fileに関するあれやこれ

「Ioが面白い」と言って3月に集中的に学習してブログにも書きまくってましたが、一段落ついてみると積極的に使う場面がなくてなんとなく放置。そのあいだにLuaを使うことになったり、本業のC++をやっていたりとしたわけですが。なにやら。アクセスログを見ていると割と定期的にIoの記事を見にきてくださっている方がいらっしゃるようで。それを見ると、新しいネタ提供できてないのが申し訳ないなーという思いがわいてきて、同時に、Ioでまだ学び足んないところあるんだよなーと考えていたことが思い起こされて。
そんなこんなで、Io再起動です。


今回は。プログラミングする上でほぼ必須と言える機能、ファイルの入出力について。

基本の操作

ファイル操作の基本は、(1)Fileオブジェクトのクローンを作る、(2)クローンにファイルパスをセットする、(3)ファイルを開く、(4)ファイルの内容を操作する、(5)ファイルを閉じる、という流れになります。


実例。

file := File clone                       // クローンを作る
file setPath("foo.txt")                  // ファイルパスをセットする
file open                                // ファイルをオープンする
file write("hoge\n", "fuga\n", "uhyo\n") // ファイルに文字列を書き出す
file rewind                              // アクセス位置をファイルの戦闘に巻き戻す
while(line := file readLine,             // ファイル内の文字列を行単位で読み込む
  line println
)
file close                               // ファイルを閉じる

実行結果。

$ io sample0.io 
hoge
fuga
uhyo


この操作ではファイルが存在しないばあい、新しくファイルが作成されました。すでにファイルが存在していて内容がある場合はどうなるか。


すでに内容があるファイルを用意します。

$ cat foo.txt 
HOGE
FUGA
UHYO
HOGE
FUGA
UHYO


実行結果。

$ io sample0.io 
hoge
fuga
uhyo
HOGE
FUGA
UHYO

ファイルのサイズはそのままで操作をした部分だけが書き換えられているのがわかります。


ファイルのクローン、パスのセット、ファイルのオープンまではセットで扱うことが多いので、メソッドチェーンにして次のように書くことが多いようです。

file := File clone setPath("foo.txt") open


さらに。クローンとパスのセットをセットにしたwithメソッドを使うとさらに見やすくなります。

file := File with("foo.txt") open

openFor〜

openメソッドはファイルの書き換え(ファイルがないばあいは作成して書き込み)をおこないましたが、読み込みのみをしたいばあいや、既存の内容を破棄してから書き込みをしたいばあいがあります。そのばあいはopenForから始まるメソッドを使います。

メソッド名 操作内容 対象ファイルが存在しない場合
openForAppending ファイルの末尾に追加する 例外を発生させる
openForReading ファイルの内容を読み出す 例外を発生させる
openForUpdating ファイルの先頭から書き込む ファイルを作成する


openForUpdatingメソッドは実質openメソッドと同じ動作をします。

file := File with("foo.txt") openForUpdating
file write("foo\n", "bar\n", "baz\n")
file rewind
while(line := file readLine,
  line println
)
file close

実行結果。

$ cat foo.txt 
AAA
BBB
CCC
DDD
EEE
FFF
$ io sample3.io
foo
bar
baz
DDD
EEE
FFF


既存のファイルの内容をすべて破棄して一新したい場合はオープンする前にremoveメソッドで既存のファイルを破棄するようにします。removeはオブジェクト自身を返すのでメソッドチェーンに組み込むことができます。

file := File with("foo.txt") remove openForUpdating
file write("foo\n", "bar\n", "baz\n")
file rewind
while(line := file readLine,
  line println
)
file close
$ cat foo.txt 
AAA
BBB
CCC
DDD
EEE
FFF
$ io sample4.io
foo
bar
baz

バイト単位の読み出し、行単位の読み出し

foreachメソッドを使うとバイト単位で、foreachLineメソッドを使うと行単位で、ファイルを読み出すことができます。インデクスの値をとる第1引数は省略可能です。


実装。

file := File with("foo.txt") openForReading
file foreach(i, v, 
  writeln(i, ":", v)
)
file close

実行結果。

$ cat foo.txt
foo
bar
baz
$ io sample5.io 
0:102
1:111
2:111
3:10
4:98
5:97
6:114
7:10
8:98
9:97
10:122
11:10


実装。

file := File with("foo.txt") openForReading
file foreachLine(i, v, 
  writeln(i, ":", v)
)
file close

実行結果。

$ cat foo.txt 
foo
bar
baz
$ io sample6.io 
0:foo
1:bar
2:baz

中身丸ごと読み込み

ファイルの中身をごっそりメモリ上に読み込む方法は(少なくとも)3種類あります。

メソッド名 操作内容 標準入力に対して
contents ファイルの内容をひとつの文字列(バイト列)として返す 操作可能
asBuffer ファイルの内容をひとつの文字列(バイト列)として返す 不可
readLines 改行で分離した文字列のリストを返す 操作可能

標準入力を対象としない場合、contentsasBufferはほぼ同じと考えてよいようです。

標準入出力

標準入出力はFileに定義されているメソッドで対応するオブジェクトを取得できます。これらはFileのクローンなので、Fileで定義されているメソッドを利用できます。

メソッド名 内容
standardInput 標準入力
standardOutput 標準出力
standardError 標準エラー出力