最近の記事
- 9/1 - 将棋始めました
- 5/16 - サーバー引っ越し
- 4/24 - 優先順位
- 3/17 - vbNullString と 空文字列 ""
- 3/15 - InputBox 関数の戻り値
- 3/13 - 紙と Excel と VBA
- 3/9 - 未だに Visual Basic 6
- 11/14 - ぼて閉鎖
- 11/9 - 関数オブジェクトの呼び出し
- 9/7 - メソッドとしての関数オブジェクト
Entering Passive Mode
一部の高級言語には、プロパティと言う概念がある。
プロパティは、setter と getter を
特別な構文でカプセル化したものだ。
Rectangle rect = new Rectangle();
rect.width = 100;
int value = rect.width;
C# や VB など、Microsoft 系のユーザにはおなじみだが、
Java や C++、Perl などには、それらの概念がない。
さて、今までの考察を盛り込み、
細かい問題点を整理していき、
モジュールとしてまとめてみよう。
========== EncoderHandle.pm ==========
# 独自クラス。
package EncoderHandle;
# Perl 5.6 以上を要求する。
require v5.6.0;
Encode, Jcode, jcode.pl はそれぞれ呼び出し規則が異なる。
今日はそれを吸収する仕組みを作ろう。
クラスレベルでインタフェースを統一する場合、
ラッパークラスを作るのが一般的。(Adapter パターン)
今回は、変換用の関数を呼ぶだけなので、
同じ引数と戻り値を持つ関数を用意するだけでいい。
では、統一するインタフェースとして、
Encode::from_to を使うことにしよう。
この関数は、変換するデータ、
変換前の文字コード、変換後の文字コードの三つを取り、
戻り値として変換後のバイト数を返す。
失敗すると、undef を返す。
さて、モジュールのフォールバックは実装できた。
では、肝心な変換部分をどのように呼び出すか。
まず考えられるのが、読み込まれたモジュールを調べ、
それにより分岐する方法だ。
use は内部で require を呼び出している。
require で読み込まれたファイルは、
パッケージ変数 %INC に格納されている。
なので、Encode モジュールが読み込まれているかどうかは、
exists $INC{'Encode.pm'} が真を返すかどうかで分かる。
これを使えば、WRITE メソッド内で分岐させることができる。
昨日は、Encode モジュールがあることが前提だった。
そのため、Encpde がない環境で走らせると、
Can't locate Encode.pm in @INC
というエラーになってしまう。
だからといって、最も簡単に導入できる、
jcode.pl を使うと、Unicode の対応ができない。
Encode がある環境では Encode を使いたい、
ない場合は Jcode、それもなければ jcode.pl が理想だ。
つまり、「フォールバック」を行いたい。
文字コード変換をファイルハンドルに組み込んでみよう。
ファイルハンドルクラスには、
変換を行いながら出力を行うことになるので、
最低限 3 つの引数が必要だ。
・実際の出力先ファイルハンドル
・入力の文字コード
・出力の文字コード
引数は、連想配列(ハッシュ)を使って渡すことにしよう。
プログラムの見た目がすっきりするからだ。
使い手が以下のように呼び出せるように作ってみよう。
では、文字コードの変換をしてみよう。
まずは、jcode.pl から。
これはモジュールではないため、
懐かしい require 文で取り込む。
require 'jcode.pl';
文字コードを調べるには、getcode を使う。
# 'jis' / 'sjis' / 'euc' / 'binary' / undef が返る。
my $encoding = jcode::getcode(\$data)
ハンドルを乗っ取る方法はわかった。
では、文字コードの変換を考えてみよう。
(文字集合の符号化方式の変換)を考えてみよう。
日本語の文字コードには、ポピュラーなものに、
SHIFT JIS, JIS, EUC の三種類がある。
それぞれ、Windows, 電子メール、UNIX で使用されている。
それらは、利用している文字集合こそ殆ど同じなのだが、
符号化方式(エンコーディング)が異なるため、
プログラムで取り扱うには非常に面倒である。
今日は実験をしてみよう。
ファイルハンドルの tie には、
Tie::Handle という既定の実装が存在する。
この実装は、PRINT, PRINTF, GETC の呼び出しを、
WRITE と READ 用に変換して代わりに呼び出してくれるのだ。
つまり、全てのメソッドを準備する必要がなくなるので、
独自のクラスを作るためには、これを継承すると楽だ。
# 独自クラス。
package NaggingHandle;
Perl 5.8 系の IO レイヤは、
ファイルハンドルの奥底で秘密裏に処理を行う。
5.6 系で同様のことをするためには、
ファイルハンドルに細工をするのが最も速い。
tie 関数を使えばそのようなことが実現できる。
tie の仕組みを使うには、
事前にファイルハンドルとしての動作を模倣する、
パッケージ(クラス)を用意する必要がある。
tie 関数に、ファイルハンドルとパッケージ名を渡せば、
内部でクラスのインスタンスが作成され、
ファイルハンドルに関連付けられる。