第42回
■
■SqueakではじめるSmalltalk入門 第42回
■
本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。
ストリーム(a Stream)は「絶え間のないデータの流れ」を表現するオブジェクトで、磁気ヘッド(position)を使って磁気テープ(基になるコレクション)から情報を読み(next)書き(nextPut: anObject)できることを前回示しました。見方を変えると、ストリームは最後に読み出した場所を覚えていてくれる、ちょっと変わったコレクションの一種だと考えることもできます。実際、ストリームにはコレクションの特徴的なメソッドでもある#do:も定義されていて期待通りの動作をします。
| stream |
stream _ 'abcdefg' readStream.
World findATranscript: nil.
Transcript cr.
stream do: [: each | Transcript show: each asUppercase]
" => ABCDEFG (トランスクリプトへの出力) "
さて。今回は外部ストリーム、つまりファイル情報にアクセスするためのストリームについてです。と、申しましてもこのオブジェクトについては、
(FileStream fileNamed: 'test.txt') edit
のような式において、editメッセージを受けるレシーバとしてすでに登場済みです。ただ、このeditによって起動されるメソッドFileStream >> #editはファイルリスト(a FileList)というオブジェクトと連携して少々ややこしいことをしていますので、まずは、オーソドックスなファイルストリームの扱いを見てみましょう。
ファイルストリームも、前回紹介した内部ストリームとほとんど同じように扱えます。ちょっと違うのは、内部ストリームを作るときは基になるコレクションを指定したのに対し、ファイルストリームではファイル名を指定すること。また、ストリームを使い終わったらデバイスを解放するためにそれを“閉じる”必要があることです。たとえば、FileStreamExample.txtという名前のファイルを作って、そこに文字列を書き込む式は次のようになります。
| stream |
stream _ FileStream fileNamed: 'FileStreamExample.txt'.
stream nextPutAll: 'abcdefg'.
stream close
「読み」と「書き」それぞれ専用のものがあった内部ストリームと違って、ファイルストリームは読み書き共用です。nextを送れば、データの読み出しもできます。
| stream |
stream _ FileStream fileNamed: 'FileStreamExample.txt'.
World findATranscript: nil.
Transcript cr.
[stream atEnd] whileFalse: [Transcript show: stream next asUppercase].
stream close
もちろん必要ならば読み出し専用のストリームにもできますが、モードのようなものを切り換えているだけで、内部ストリームのように属するクラスを使い分けているわけではありません。
| stream |
stream _ FileStream readOnlyFileNamed: 'FileStreamExample.txt'.
[stream nextPut: $a] ensure: [stream ifNotNil: [stream close]].
" => Error: Cannot write a read-only file "
(FileStream fileNamed: 'FileStreamExample.txt') class
" => MultiByteFileStream "
(FileStream readOnlyFileNamed: 'FileStreamExample.txt') class
" => MultiByteFileStream "
ここで、FileStreamにメッセージを送っているのに、出てきたストリームがMultiByteFileStreamのインスタンスであるというのはオカシイ…と気付かれた方もおられることと思います。この疑問はFileStream >> #fileNamed:をブラウズするとすぐに解決できます。
FileStream >> fileNamed: fileName
^ self concreteStream fileNamed: (self fullName: fileName)
self、つまり、この文脈ではFileStreamというクラス自身にconcreteStreamというメッセージを送って、その返値に改めて「fileNamed: ...」というメッセージを送りなおしています。concreteStreamの送信に対する返値がおそらくMultiByteFileStreamで、FileStream fileNamed: ... は、実際にはMultiByteFileStream fileNamed: ...と等価なのだろう…ということは容易に想像できると思います。このことを確認するには、FileStream >>#concreteStreamの定義を見ます。FileStream >> #fileNamed:を閲覧中のブラウザで、implementorsボタンのポップアップから「concreteStream」を選ぶなどして、新しいブラウザを開いてみてください。どうです? 予想どおりですね。
FileStream >> concreteStream
^ MultiByteFileStream
さて。日本語の書き出しもしてみましょう。
| stream |
stream _ FileStream fileNamed: 'NihongoExample.txt'.
stream nextPutAll: '日本語の文字列'.
stream close
内容を確認してみます。
| stream contents |
stream _ FileStream fileNamed: 'NihongoExample.txt'.
contents _ stream contents.
stream close.
^ contents " => '日本語の文字列' "
問題ないですね。だだ、SqueakNihongo7はデフォルトでは文字コードにUTF-8を使って日本語文字列を出力するので、シフトJISを期待する他のソフトでは文字化けしてしまいます。
a MultiByteFileStreamでは、読み書きにテキストコンバータ(a TextConverter)というオブジェクトが介在し、あらかじめ決められた文字コードで読み書きが行なわれています。
(FileStream fileNamed: 'test.txt') converter
" => an UTF8TextConverter "
Mac OSではシフトJISが多く使われるので、an UTF8TextConverterの代わりにシフトJISを理解するテキストコンバータに代えてやれば、UTF-8からの変換の手間を省くことができて便利です。
| stream |
stream _ FileStream fileNamed: 'ShiftJisExample.txt'.
stream converter: ShiftJISTextConverter new.
stream nextPutAll: 'シフトJISの日本語文字列'.
stream close
TextConverterのサブクラスを見れば、使用可能なコンバータを一覧できます。
TextConverter subclasses
" => #(CompoundTextConverter EUCTextConverter MacRomanTextConverter
ShiftJISTextConverter UTF8TextConverter Latin1TextConverter
CP1253TextConverter ISO88597TextConverter UTF16TextConverter
CP1250TextConverter ISO88592TextConverter) "
このページを編集 (5309 bytes)
|
以下の 1 ページから参照されています。 |
This page has been visited 998 times.