ファイルの扱い方

去年の後半は全くというほど Smalltak を使っていなくって、しかも C++ 関連で覚えたことが結構ボリュームがあったので、容量の逼迫している猫の脳内では上書き保存が進行しています。

で、今になって「Smalltalk分が足りないっ!」と、Squeak でいろいろやろうとしたのですが、う・・・、アレなんだっけ、コードが出てこない orz。なんかもう、いろいろ忘れてしまったので、もう一度勉強しなおしです。

基本

ファイルの読み書きには FileStream を使用します。

"== ファイルを開いて普通に書き込む (R/W) =="
| file |
file _ FileStream fileNamed: 'foobar.txt'.
file nextPutAll: 'hogehogeほげほげ123'.
file close

Read / Write なファイルストリームを作るには #FileNamed: メッセージを送ります。ちなみに 実際に返ってくるのは MultiByteFileStream のインスタンス などになります。 (Squeak のバージョン毎に異なるかな?)

ファイルに何かを書き込むには #nextPut: または #nextPutAll: を使います。 これは Stream に共通の操作です。

今度は読み込みます。

"== ファイルを開いて読み込む =="
file _ FileStream readOnlyFileNamed: 'foobar.txt'.
Transcript cr; show: file contents.

[file atEnd] whileFalse:
        [Transcript cr; show: ' >'; show: file next].

file reset.
Transcript cr; show: ' >'; show: (file next:5).
Transcript cr; show: ' >'; show: (file next:5).
file close

先ほどの #FileNamed: でも構わないのですが、#readOnlyFileNamed: を使うと 書き込もうとしたときに Cannot write a read-only file と言うエラーが発生します。

内容の読み出しは #contents で全部、#next で一個づつ読み出し になります。 #atEnd と併せてループを回しながら1文字づつ読み出すことも出来ますが、 ちょっとかっこわるいコードですね。 以下、実行結果です。

hogehogeほげほげ123
 >h
 >o
 >g
 >e
 >h
 >o
 >g
 >e
 >ほ
 >げ
 >ほ
 >げ
 >1
 >2
 >3
 >hogeh
 >ogeほげ

文字コード

というのは、Squeak から書いて Squeak から読み込む場合。 日本語のファイルの場合、エンコードが重要になってきます。

"== Shift-JIS のファイルを書く =="
file _ FileStream fileNamed: 'foo-sjis.txt'.
file converter: (TextConverter newForEncoding: 'shift-jis').
file nextPutAll: '吾輩は猫である。名前は未だ無い。'.
file close.

"== Shift-JIS のファイルを読む =="
file _ FileStream readOnlyFileNamed: 'foo-sjis.txt'.
file converter: (TextConverter newForEncoding: 'shift-jis').
Transcript cr; cr; show: file contents.
file close.

ようするに、コンバータを設定してあげれば問題なしです。

実践

FileStream は使いにくいことに いちいち close しなければいけません。 しかし、たとえちゃんと ファイル操作の末尾に close を書いたとしても 途中で例外で落ちてしまったら、やはりファイルハンドルを 握りっぱなしになってしまいます。

そこで、ファイルを扱うときは普通こうします。

"== 実践 =="
file _ FileStream readOnlyFileNamed: 'foo-sjis.txt'.
[   file converter: (TextConverter newForEncoding: 'shift-jis').
    Transcript cr; cr; show: file contents
] ensure: [file close]

FileMan

FileMan は 梅澤さんが作った いけてるファイル操作ライブラリです。通常操作するには以下の二つのクラスのみを知っていれば OK。

  • FmFileEntry
  • FmDirectoryEntry

基本的に、'パス文字列' asFileEntry とか 'パス文字列' asDirectoryEntry で この子達をつくってあげて、そこからいろいろ操作してあげる格好です。また、FmDirectoryEntry は ディレクトリを 辞書 の様に扱えるのが特徴で、#at: や #at:put: で 読み込んだり書き込んだりできます。たとえば、

'./subDir' asDirectoryEntry at: 'foo.txt' put: 'Hello, world!'

とすると、

./
 └ subDir
     └ foo.txt        #←これの中身が 'Hello, world!'

となります。

FileMan は本当に気の利いたライブラリで、

'./subDir' / 'hoge/piyo' /  'foo/var' / 'xyzzy'

"print it->" C:\Squeak3.8\subDir\hoge\piyo\foo\var\xyzzy

と、 / メッセージでディレクトリの連結が出来ちゃったりします。ライブラリというよりは ファイル操作 DSL という趣が心地よいです。


・・・なぁんて、あたしのエントリを読むよりも、FileManのことはこちらを見てしまった方が早いし解りやすいです。

FileManで楽々ファイル操作(PDF)
ftp://swikis.ddo.jp/SqueakDevJa/events/SqueakersNight2007/FileMan.pdf


しかも

  • 日付フォルダをつくってあるディレクトリ以下をバックアップ
  • 既存のエラーログファイルに逐次追記
  • 100日以上更新していないファイル群の削除
  • 文字コード・改行コード変換

といった 直ぐに使えるサンプルコード付き!


ところで

FileMan で #at:put: で日本語を読んだり書いたりすると UTF-8 になるのですが、Windows だとやっぱり Shift-JIS で扱いたい。ちょっとだけなら

"== sjis 書き込み =="
'./subDir' asDirectoryEntry
    binaryAt: 'bar.txt' 
    put: ('こんにちは、世界!' convertToEncoding: #sjis)

"== sjis 読み込み =="
('./subDir' asDirectoryEntry binaryAt: 'bar.txt') asString
    convertFromEncoding: #sjis

みたいに binary で読み書き + 手前エンコード とか、または FileStream にしたりすればいいけれど、Shift-JISをよく使うのなら FmFileEntory/DirectoryEntory の派生版を用意しておくと便利かもです。

追記(2008-03-17)

とか書いたら、梅澤さんから「最新のFileMan はエンコード対応してますよ」とのコメント。ありがとうございます。うー、ごめんなさい。また確認不足で嘘情報を書いてしまいましたよ、あたし...orz

というわけで、最新の FileMan では

'./subDir' asDirectoryEntry 
    at: 'bar.txt' put: 'こんにちは、世界さん' by: 'shift-jis'

'./subDir' asDirectoryEntry 
    at: 'bar.txt' by: 'shift-jis'

"print it==>" 'こんにちは、世界さん'

と、Shift-JIS ファイルの扱いも簡単です。