RealBasic University

このコラムはStone Table Softwareのオーナーであり、またREALbasic Developerの編集者でもあるMarc Zeedar氏により書かれたものを、著者の許可を得て翻訳したものです。この翻訳はHREM Researchにより提供されています。この日本語版へのご意見はRBU-Jまでご連絡下さい。

URL: http://www.applelinks.com/rbu/051/
INDEXに戻る

SpamBlocker:パート II

私たちは先週から、HTMLページに埋め込まれたメールアドレスのリンクを覆い隠して、スパムメールを防ぐプログラムであるSpamBlockerを始めました。先週は、インターフェースを構築し、メインウィンドウにテキストファイルをドラッグ&ドロップするプログラムまで仕上げました。今回は目的の文字列を探し出し、スパムロボットに認識できないような訳の分からない記号のメールアドレスに変換するコードを仕上げることにしましょう。

ParseFileメソッド

ParseFileはこのプログラムの中心の部分に当たります。これは指定したファイルの中を検索し、"mailto"のURLを目的に適うように変換します。

順を追って説明すると、次のとおりになります。

  1. テキストファイルをロードします。
  2. "mailto"の参照部分をすべて見つけて、目的のものに変換します。
  3. 新しいファイルとして保存します。

まず始めに新しいmethod( Editメニューの"New Method")を作成し、parseFileと命名しましょう。 パラメータの部分にはf as folderItemと入力して、OKをクリックしましょう。

以下にコードを示します。

   dim in as textInputStream 
dim out as textOutputStream
dim s, searchString, replaceString as string
dim i, j as integer

searchString = "<a href=" + chr(34) + "mailto:"
in = f.openAsTextFile
if in <> nil then
s = in.readAll
in.close

i = inStr(s, searchString)
while i > 0
replaceString = ""
i = i + len(searchString)
j = inStr(i, s, chr(34))
if j > 0 then
// Found one, so convert it
replaceString = "mailto:" + mid(s, i, j - i)
replaceString = convertString(replaceString)

// Insert it
s = mid(s, 1, i - len("mailto:") - 1) + replaceString + mid(s, j)
end if

i = inStr(i, s, searchString)
wend

out = f.createTextFile
if out <> nil then
out.writeLine s
out.close
end if
end if // in = nil

なんだかたくさんありますね。しかし、一つ一つ見ていけばそれほど難しくはありませんよ。

まずプログラムでは作業で用いる変数を定義しています。それから、実際に<a href="mailto:を検索するテキストにsearchStringをセットします。ここでchr(34)は " のような二重引用符であることに注意してください。

次に、ファイルをテキストファイルとして開く準備をします。変数のintextInputStreamオブジェクトです。何かの理由でファイルが開けなかった場合、innilにセットされてそのファイルをスキップします。nilでなければそのまま処理を続行します。

最初にファイルの全内容を読み出し、sにそのテキストを代入します。それから用の済んだinオブジェクトを閉じます。(技術的には、ファイルはREALbasicによって自動的に閉じられます。しかし、自分自身で閉じておくのは良い習慣です。)

さて次に別のループを始めましょう。これはs(ファイルの中身)にあるsearchStringが見つからなくなるまで検索し続けます。これを、結果にiを割り当てることによってinStr()ファンクションに伝えます。inStr()は目的のものが見つからなかった場合、0(ゼロ)を返します。その他の場合は、目的の文字列がどこから始まるかの文字位置を返します。

詳細

inStr()に慣れるために、次のコードを入力して試してみるといいでしょう。

   // 2を返す 
msgBox str(inStr("Mischief is my cat.", "is"))

// 9を返す
msgBox str(inStr("Mischief is my cat.", " is"))

// 1を返す
msgBox str(inStr("Mischief is my cat.", "Mischief"))

// 9を返す
msgBox str(inStr("Mischief is my cat.", " "))

// 0を返す
msgBox str(inStr("Mischief is my cat.", "q"))

検索する文字列が見つかったとすれば、iはその文字列の始まる数を表します。次に私たちは"mailto"URLの終わり部分を探したいですよね。HREFの要素は二重引用符(")で囲まれているので、その二重引用符(chr(34))を検索します。しかしi番から始めると、searchString内の二重引用符(")で検索が終わってしまうので、それはやめておきましょう!

そこでi番にsearchStringの長さを加えて、searchStringの終わりの部分から検索し始めることにします。そしてその結果をjに代入します。いまi番からj番の範囲はメールアドレスを表していて、この二つの変数の差がメールアドレスの長さになります。

そこで得た情報を用いて、mid(s, i, j - i)によってメールアドレスの部分を抽出します。これによってi番目から始まる文字列から、j - i文字を抜き出します。

詳細

分かりにくいかも知れないので、図で示しましょう。

上のテキストが私たちのファイルだと思ってください。iは19文字目で、それにsearchStringの長さを加えると35になります。あとの二重引用符は51文字目なので、51-16=35から、35文字目から始まる16文字を抜き出せば、黄色い部分のテキストを取り出すことができます!

変換に必要なテキストを手に入れたあとは、それを実際にメールアドレスを符号化してくれるconvertStringに渡します。

新しい文字列は次のようにファイルに挿入されます。

 s = mid(s, 1, i - len("mailto:") - 1) + replaceString + mid(s, j) 

始めのmid()関数は、ファイル始めの部分(発見箇所iまでのすべて)を取り出します。そして"mailto:"を差し引くことによってオリジナルファイル中の"mailto:"を取り除き、そこにconvertStringで符号化されたものを使用します。そこで符号化されたreplaceStringを加え、ファイルに残りのテキストを追加します。(mid()に返す文字数を指定していない場合は、文字列最後までの全部を返すことを覚えておいてください。)

このすべての結果は、符号化されたメールアドレス自身を除いては、以前と同じものをすべて含んでいる新しいsを与えます。

それから、別の"mailto"部分を探すために、また改めて検索を始めます。

私たちの最終ステップは、新しいファイルを出力することです。outtextOutputStreamオブジェクトに設定し、もしそれがnilでなければ、そこにテキストを出力します。それが有効なtextOutputStreamであれば、処理した古いファイルを削除しながら新しいsをファイルに書き込んでいきます。

重要事項特にこのプログラムについては、私はオリジナルファイルに上書きすることにしましたが、これは賢明な方法ではありません。SpamBlockerを動作確認するときに、正常に動作していることを確認するまでは、オリジナルファイルではなく、テストファイルあるいはバックアップファイルでテストしてください。いったんファイルに上書きしてしまうと、元に戻す方法はないですよ!

The ConvertStringメソッド

もうほとんど終わりました。あとはメールアドレスを実際に符号化する方法を書く必要がありますが、これはとても簡単です。

はじめに、新しいメソッド(Editメニューの,"New Method")を作成し、次のように設定してください。

次に、以下のコードを加えてください。

   dim i, n as integer 
dim s as string

s = ""
n = len(theString)
for i = 1 to n
s = s + "&#" + str(asc(mid(theString, i, 1))) + ";"
next

return s

んー……なんか複雑ですね! ;-)

ご存知のように、theStringとして変換したい文字列をこれに通します。その文字列の長さを入手し、1からその長さまでfor-nextループを設定します。それから、文字列の中の各文字を調べ上げます。

各文字については、その文字の前の部分に"&#"を、さらに文字のASCII番号、最後にセミコロン(;)を付け加えておきます。ここでmid()は文字列の中の一文字を返し、Asc()はその文字のASCII番号を返します。Str()は返されたASCII番号を符号化された文字列に変換するので、それをsに付け加えます。最後に、セミコロン(;)をつけて終了しましょう。

theStringの中の文字をすべて処理し終わったならば、HTMLとして符号化された同じ文字列を含むsを返します。単純ですね!

これで殆ど終わりです。それではプログラムを保存し、実行して見てください。あなたがSpamBlockeのテストに使えるように、testfile.htmlを作成しました。変換後はこんな感じになります。

なんだか奇妙に見えますが、メールアドレスのリンクは変わらず機能しますよ!これがスパムメールを防止するのにどれほど有効かは別問題ですが、害にはなりません。

SpamBlockerの拡張

私はこれまでREALbasicの中でSpamBlockerを実行してきましたが、あなたがお望みなら、スタンド・アロンのアプリケーションとしてディスクにコンパイルすることができます。その時には、SpamBlockerウィンドウではなく、SpamBlockアイコンの上に直接ファイルをドラッグ&ドロップできるようにしたいと思うでしょう。

この問題はあなたにお任せしますが、少しだけヒントをあげましょう。

考えられる別の改良点は、SpamBlockerをドロップされたフォルダーに対応させることです(いまはテキストファイルのみです)。それをするためには、新しいフォルダー・ファイル・タイプ(folder file type)(ポップアップメニューを用いて、"special/folder"項目を選択します)を加え、ウィンドウにフォルダーのドロップを受け付けるよう伝えます。それから、再帰的にドロップされたフォルダーの内容を解析していきます。

そこで、あなたにやる気があるならば、これらの改良は宿題にします。

今週のREALbasicチュートリアルの完全なプロジェクト・ファイル(リソースを含む)を手に入れたい場合は、ここからダウンロードしてください。

次週

もちろん、なにかとても面白いものです!

Letters

今週の一番目の手紙は、先週のコラムに関係があります。REALbasicの権威、Thomas Tempelmannは、専門的なASCIIの定義に関する問題について注意してくれました。

ASCIIコードは0〜127までの"単純な"U.S.文字を定義するだけで、それ以上はありません。

だから、#169というコードを定義しているASCIIテーブルはないので、それを呼び出すことはできません。いちばんの方法は、たとえANSIとISOがそれぞれの標準規則を持っていたとしても、ANSI8ビット文字セットを呼び出すことだろうと思います。よって、正しい引例はANSI 8997などのようになるでしょう。ANSIとISOの番号はちょっと忘れてしまいましたが…。

Thomas

Thomasさん、明確にしてくれてどうもありがとうございます。ASCIIについての私の解説は、REALbasic用語集により詳しく載っていますが、私はすべての文字番号をASCIIの中にひとかたまりにしてしまっているので、専門的には正確ではありません。

次は、お医者さんからの質問です。

こんにちは。

私はいくつかの新しい方法について学んでいる昔のプログラマー(FORTRAN、Pascal、BASIC時代…) です。医学的な事務作業をもっと効率的にするために、2、3のプロジェクトへREALbasicの導入を検討しています。当然のことながら、それらの仕事を効率よくこなし、十分使いものになるようにできれば、それを他の医師に売り込むことも考えています。

簡潔に言うと私の問題はこうです。処方箋や記録管理などのために特定の薬が簡単に選択できるように、階層的方法で薬剤名のデータベースを導入したいのです。階層は次のようになります。

 
   DrugClass0 
DrugSubClass0
DrugName0
DrugName1
...
DrugNameN
DrugSubClass1
DrugName0
DrugName1
...
DrugClass1
DrugSubClass0

私は薬の選択に、OS Xの"コラムビュー(column view)"のようなものを導入するためのデータベース構造とリアルベーシックコードを探しています。これに適したものがRBの世界にあるかご存知でしょうか?

また、ユーザが薬剤名を入力すると入力された始めの数文字から判断して薬剤名のポップ・アップ・リストをソフトウェアが自動的に作成(あるいは薄い色の文字で表示)してくれるようにしたいのです。私が何をしたいか分かりますよね。これが一番目の質問と違うことは承知しています。私はデータベースの実現と、入力事項の補完のためにワイルドカードを用いたSQL選択文を使用して、なにか可能な方法がありそうな気がします。私の探しているコードがあるかご存知でしょうか?それがあれば、労して再開発せずにすむのですが。

親切な力添えどうもありがとうございます!!

Harlan R. Ribnik, M.D.

残念ながら、あなたの探している"コラム・ビュー"構造のようなものは私はいままで見たことがありません。それはとても良いものだと思うし、これができるクラスを誰かがすでに開発しているかもしれませんが、私は知りません。かといって、それが存在しないと言っているわけではありませんので、もしどなたかMac OS Xのコラム・ビューを実行できるREALbasicクラスをご存知でしたら教えてください。ここで公表することにしましょう。

その間に、ユーザの入力に応じて変わる動的リストボックスが使えるでしょう(タイプした文字に一致する名前を表示するクラリスメイラーのアドレスブックに似ています)。

しかし、あなたの二つめの質問には助けになれますよ。私のGrayeditクラスによって、あなたのプロジェクトに自動補完機能を付け加えることができます。 それにリストの名前を登録しておけば、ユーザがある名前を入力し始めたときに、次のように補完された名前が灰色で表示されます。

タブキーを押せば、灰色で補完された名前が黒で表示されます。

Grayeditは無料のクラスファイルなので、サンプル・プロジェクトをダウンロードして試してみてください。 しかしそれには多数の名前が必要になるので、SQLデータベースから動的にリストを生成するメソッドを作成すべきでしょう(これはそう難しくはないと思います)。


RBU-Jの通知サービス!コラムが発表されるたびに日本語版REALbasic Universityのお知らせの emailがあなたに届きます。登録・削除は ここ から。

INDEXに戻る