2008年11月08日

編集距離(レーベンシュタイン距離)の計算

文字列間の類似度を求める方法の一つとして、編集距離が挙げられます。編集距離は、考案者にちなみレーベンシュタイン距離とも呼ばれますが、具体的には、挿入や削除、置換によって、一方の文字列から他方の文字列に変換するために必要な作業の最小回数です。

use List::Util;

sub levenshtein_distance {
  my ($list_1, $list_2) = @_;

  my $len_1 = scalar(@{$list_1});
  my $len_2 = scalar(@{$list_2});

  my @d;
  foreach my $i (0 .. $len_1) {
    $d[$i][0] = $i;
  }
  foreach my $j (0 .. $len_2) {
    $d[0][$j] = $j;
  }

  foreach my $i (1 .. $len_1) {
    foreach my $j (1 .. $len_2) {
      $d[$i][$j] = List::Util::min(
        $d[$i - 1][$j] + 1,
        $d[$i][$j - 1] + 1,
        $d[$i - 1][$j - 1] + (($list_1->[$i - 1] eq $list_2->[$j - 1]) ? 0 : 1),
      );
    }
  }

  return $d[$len_1][$len_2];
}

Perl で書いてみるとこんな感じかな(Perl 5.8 標準ライブラリの List::Util が必要です)。配列のリファレンスで文字列を表現し、引数として受け取ります。文字列をスカラーで渡さないのは、文字列以外にも何らかの順序配列の編集距離を計算するのに利用するためです。

my $list_1 = [ 't', 's', 'u', 'k', 'u', 'b', 'a', ];
my $list_2 = [ 't', 'o', 'k', 'y', 'o', ];

print levenshtein_distance( $list_1, $list_2);

実際の利用例はこんな感じ。単純にテキスト間の編集距離を求めたいのであれば、Text::Levenshteinを利用するのが良いと思います。

この関数は、ウェブページを HTML 構造に基づきクラスタリングするために作成しました。HTML は DOM Tree と呼ばれる木構造で表現できますが、この木からブロック要素部分を後順走査で探索し(実際は HTML タグを上部から探索するので結果的に後順走査になるだけ)、その結果を配列として表現します(タグ名の配列を作る)。その配列同士の編集距離にを求めることは、HTML 構造に基づく類似度を求めることであり、結果的に HTML 構造に基づくクラスタリングが出来るのでは無いかと考えています。まぁ。ブラウザのレンダリングに大きな影響を与えないような部分木が存在する可能性もあり、要検討なのですがね。

木の編集距離の文字列の編集距離による近似という論文を見つけ、調査中。日本語論文だと思っていたら、英語論文だったので涙目(発表者が日本人で日本語タイトルなのに)。

【関連記事】
コサイン尺度(コサイン類似度)の計算 (2008年11月04日)

【関連情報】
・レーベンシュタイン距離 - Wikipedia
 http://ja.wikipedia.org/wiki/レーベンシュタイン距離

22:14 | コメント (0) | トラックバック

2008年11月04日

コサイン尺度(コサイン類似度)の計算

文書間の類似度を求める方法の一つとして、コサイン尺度が挙げられます。コサイン尺度とは、2つのベクトルのなす角度であり、文書をベクトル化することにより、文書間の類似度を求めることが出来ます。

sub cosine_similarity {
  my ($vector_1, $vector_2) = @_;

  my $inner_product = 0.0;
  map {
    if ($vector_2->{$_}) {
      $inner_product += $vector_1->{$_} * $vector_2->{$_};
    }
  } keys %{$vector_1};

  my $norm_1 = 0.0;
  map {
    $norm_1 += $_ ** 2
  } values %{$vector_1};
  $norm_1 = sqrt($norm_1);

  my $norm_2 = 0.0;
  map {
    $norm_2 += $_ ** 2
  } values %{$vector_2};
  $norm_2 = sqrt($norm_2);

  return ($norm_1 && $norm_2) ? $inner_product / ($norm_1 * $norm_2) : 0.0;
}

Perl で書いてみるとこんな感じかな。ハッシュのリファレンスでベクトルを表現し、引数として受け取ります。返値は計算したコサイン尺度となります。ベクトルの各要素の値が0以上であるならば、コサイン尺度は 0 から 1 の値を取り、1に近いほど類似性が高いことを示します。

my $vector_1 = {
  '日本' => 1,
  '今日' => 3,
  '高校' => 2,
  '国語' => 1,
};

my $vector_2 = {
  '日本' => 2,
  '明日' => 1,
  '大学' => 1,
  '数学' => 1,
};

print cosine_similarity($vector_1, $vector_2);

実際の利用例はこんな感じ。文書のベクトルには、出現した単語数を利用すると良いと思います。

卒業研究では、ウェブページ内の重要箇所特定(本文抽出)に取り組んでいるのですが、コサイン尺度は、重要箇所候補の重要度を求めるために利用します。一先ず、参照論文 Automatic Identification of Informative Sections of Web Pages の ContentExtractor を実装しているのですが、実験結果に書かれている実行速度が出ない…。重要箇所候補数を載せておいて欲しかったかな(区切りとなるタグもあやふやですし)。プロファイラ Devel::DProf, Devel::SmallProf で調べてみると、この類似度計算がボトルネックになっていることが判っています(呼び出し回数が多いというのもある)。ハッシュ操作の keys, values で詰まる感じ。他の類似度計算手法も調査中。

【関連情報】
・Cosine similarity - Wikipedia
 http://en.wikipedia.org/wiki/Cosine_similarity

【関連商品】
情報検索アルゴリズム (北 研二, 津田 和彦, 獅々堀 正幹)

22:32 | コメント (0) | トラックバック

2008年06月18日

IANA から TLD の情報を抽出するスクリプト

TLD の組織名や URL を抽出するスクリプトを書きました。Whois Gateway を作成中なのです。数年前にも作成していたのですが、頓挫していました。

use strict;
use warnings;

use LWP::Simple;

my $base = 'http://www.iana.org/domains/root/db/';

my $html = get($base);

my $re =
    '<td><a href="/domains/root/db/([\w\.-]+)">([^<]+)</a></td>' .
    '<td>([^<]+)</td><td>([^<]+)<';

while ($html =~ /$re/go) {
    my $tld  = $2;
    my $type = $3;
    my $org  = $4;

    my ($url, $whois) = get_tld_info(get($base . $1));

    printf("%s\t%s\t%s\t%s\t%s\t\n", $tld, $type, $org, $url, $whois);

    sleep 2;
}

sub get_tld_info {
    my ($html) = @_;
    my $url   = '';
    my $whois = '';

    if ($html =~ m|<b>URL for registration services:</b> <a [^>]+>([^<]+)</a>|) {
        $url = $1;
    }

    if ($html =~ m|<b>WHOIS Server:</b> ([^<]+)<|) {
        $whois = $1;
    }

    return $url, $whois;
}

ご利用は自己責任で…。

作成している Whois Gateway は CPAN の Net::Whois::Raw を利用していますが、拡張性がイマイチです。特に HTTP 経由で情報を取得する TLD を追加する場合、基のソースを弄らないといけない(オーバーライドで一部だけ弄れるのかな)。また、設定されている WHOIS Server が少なかったり古かったり。

パッチファイル作成にチャレンジしてみようかな。

【関連情報】
・IANA — Root Zone Database
 http://www.iana.org/domains/root/db/
・Walery Studennikov / Net-Whois-Raw - search.cpan.org
 http://search.cpan.org/dist/Net-Whois-Raw/

23:41 | コメント (0) | トラックバック

2008年01月14日

LWP::UserAgent で長いヘッダを受け取る方法

Simple FON Maps を改良していたときに発生した問題。現在の FON Maps では、座標情報を X-Json というヘッダに格納して通信しています。

my $ua  = LWP::UserAgent->new(keep_alive => 1);
my $data = $ua->head($url)->header('X-Json');

このコードで概ねデータを取得できていたのですが、たまに、データを取得できていませんでした。

warn($ua->head($url)->status_line);

通信で何らかのエラーが発生している可能性があり、デバッグ用にコードを追加。

500 Line too long (limit is 4096)

どうやら、ヘッダの長さが長すぎるようです。

The "500 Line too long (limit is 4096)" is generated internally in Net::HTTP when one of the header lines of the response are longer than this.

日本語圏では、このエラーに関する対処方法が見つからなかったのですが、公式なメーリングリストで対処方法が記載されていました。

my $ua  = LWP::UserAgent->new(keep_alive => 1);
push(@LWP::Protocol::http::EXTRA_SOCK_OPTS, MaxLineLength => 16 * 1024);

MaxLineLength に値を追加して解決。データを取得できないときもエラーログに残っていなかったため、気付くのが遅くなりました…。

【関連情報】
・Re: Fw: LWP Problem: Error: 500 Line too long (limit is 4096)
 http://www.mail-archive.com/libwww@perl.org/msg03754.html

02:59 | コメント (0) | トラックバック

2007年07月29日

COUNT(*) の代わりに FOUND_ROWS() を利用する

MySQL データベースからデータを取得するとき、オフセット(LIMIT)を利用する場合が多いと思う。同時に、条件に合致するデータの件数を知りたい場合も多いだろう。そのような時、そのようなクエリを投げれば良いのだろうか?

・データ取得

SELECT
  *
FROM
  table
WHERE
  field = 5
LIMIT 0, 10

・件数取得

SELECT
  COUNT(*)
FROM
  table
WHERE
  field = 5;

LIMIT を利用してデータを取得した後、別途 COUNT() 関数を利用したクエリを投げる事により、合致するデータの件数を知ることが出来る。このような単純なクエリの場合、各句をそれぞれ変数に格納していれば、特に件数取得用のクエリ文生成が面倒になることは無いだろう。

$select_data =
  'SELECT '.
  '  * ';
$select_count =
  'SELECT '.
  '  (*) ';
$from =
  'FROM '.
  '  table ';
$where =
  'WHERE '.
  '  field = 5 ';
$limit =
  'LIMIT 0, 5 ';
$sql_data = $select_data . $from . $where . $limit;
$sql_count = $select_count . $from . $where;

単純なクエリの場合は、この方法でも良いのだが、複雑になったらどうだろうか?

・データ取得

SELECT
  COUNT(*), field2
FROM
  table
WHERE
  field1 = 5
GROUP BY
  field2
LIMIT 0, 10

・件数取得

SELECT
  COUNT(DISTINCT field2)
FROM
  table
WHERE
  field1 = 5

GROUP BY 句がデータ取得のクエリ文に入る場合、前記のように各句を変数に格納していても、単純に件数取得用のクエリ文を生成することが出来ない。なぜなら、件数をカウントする COUNT(*) が GROUP BY でまとめた結果をカウントしてしまうからだ。正常にカウントするためには、GROUP BY を外す必要があり、データ取得用のクエリ文と、件数取得用のクエリ文は大きく異なる事になる。これでは、保守性が非常に悪い。今回の場合は DISTINCT 関数を用いれば解決できるが、複数のテーブルを結合したり HAVING が含まれたりする場合は、そう単純じゃない(はず)。

MySQL 4 から、新たに FOUND_ROWS() という関数が追加された。この関数を利用すると、簡単に合致するデータの件数を取得することが出来る。

・データ取得

SELECT SQL_CALC_FOUND_ROWS
  COUNT(*), field2
FROM
  table
WHERE
  field1 = 5
GROUP BY
  field2
LIMIT 0, 10

・件数取得

SELECT
  FOUND_ROWS();

データ取得用のクエリ文に SQL_CALC_FOUND_ROWS を付加していれば、次のクエリ文で FOUND_ROWS を呼び出すだけで、合致する件数を取得することが出来る。

SELECT SQL_CALC_FOUND_ROWS を使用している場合、MySQL は完全な結果セットにいくつ行があるか計算する必要があります。しかし、結果セットをクライアントに送る必要がないため、LIMIT なしでクエリを再度実行するより速く行えます。

結果セットを送る必要の無い COUNT() との比較については、リファレンスマニュアルには書かれていない。しかし、オープンソース情報データベース OSS iPedia での検証によれば、約 70% のパフォーマンス向上が確認できたとの事(InnoDB によるものなので MyISAM は未検証)。

検索エンジンの類を MySQL で開発している場合、この FOUND_ROWS, SQL_CALC_FOUND_ROWS の利用は必須であろう。クエリのキャッシュ(SQL_CACHE)を利用している場合も、正常に作動する。

ま。アプリケーションから MySQL にアクセスする際に、クエリ文をべた書きしているケースは少なくなってきていると思うけど。僕は、未だに DBI を利用して、べた書き…。

【関連情報】
・11.10.3. 情報関数 - MySQL 5.1 リファレンスマニュアル
 http://dev.mysql.com/doc/refman/5.1/ja/information-functions.html
・4.13.1. クエリ キャッシュの動作 - MySQL 5.1 リファレンスマニュアル
 http://dev.mysql.com/doc/refman/5.1/ja/query-cache-how.html
・MySQL 行カウント SQL_CALC_FOUND_ROWS の効果 - OSS iPedia
 http://ossipedia.ipa.go.jp/capacity/EV0603280115/

17:05 | コメント (1) | トラックバック

2007年06月21日

ユーザ情報テーブルはどういう設計が良いの?

idea*idea を見ていて思ったのですが、ユーザテーブルの設計は、どのようなのがいいのだろう。

僕は、認証情報は別テーブルにしています。

・ユーザ認証テーブル
user
- id
- email
- password

・ユーザ情報テーブル
user_detail
- user_id
- name
- profile

という感じですね。

ユーザ数が増えてきて、ユーザ情報テーブルを分割したいときに、ユーザ認証テーブルに接続先情報(ホスト名, テーブル名)を追加すれば、簡単にスケールするのではないかと思うのです。スケールのさせ方が間違ってるような気もするし、そもそもスケールさせる機会が無く、連結コストがだけがかかる状態でもあるのだけど。

皆さんは、どのような設計をしてるのでしょうか?ケースバイケースですけど。

ちなみに、連結コスト(クエリ文の複雑さ)の関係で、認証情報とユーザ情報を同テーブルにしようかと思っているのですが :) 今後の開発分ね。

【関連情報】
・CakePHPでSNSっぽいものを作ろうとして挫折するまでのコーディング日記(#006) - idea*idea
 http://www.ideaxidea.com/archives/2007/06/cakephpsns006.html

22:30 | コメント (0) | トラックバック

2007年03月25日

任意のディレクトリ以下のファイル一覧

テンプレートファイルの読み込みのために作った関数。指定したディレクトリ以下のファイルパスを配列として取り出す関数です。

use File::Find;

sub find_file {
    my ($dir) = @_;
    my @filelist;

    find(
        sub {
            unless (/^\./si) {
                if (-f $File::Find::name) {
                    push @filelist, $File::Find::name;
                }
            }
        },
        $dir
    );

    return @filelist;
}

ドット . で始まるファイル(ディレクトリ)は除外しています。

最初は、自前で再帰処理も加えていました。関数 find は、ちゃんと再帰処理をしてくれる実装でした。重複するパスが多くておかしいと思ってましたよ。再帰処理をしないんだったら opendir で書けば良いですしね…。

未だに OOP で Perl を書かないのは、既に旧人類でしょうか?

19:51 | コメント (0) | トラックバック

2007年03月24日

Vista で window.open.close をするには?

ファイルをダウンロードさせつつ、解説ページを開くようなリンクを JavaScript の onlick を使って書いてみました。

<a href="[NoteURL]" onclick="window.open('[FileURL]', 'dl').close();">

一瞬 [FileURL] が開かれて、直ぐに閉じられます。もちろん、ページは [NoteURL] に切り替わります。

IE 7 with XP や Firefox 2 では期待通り動いたのですが、なぜか IE 7 with Vista では動きませんでした。自動的にウィンドウが閉じられません。同名の IE 7 でも、挙動が違うようです…。

同様の機能を IE 7 with Vista で実現できる方法をご存知の方は、教えてくださいませ。

23:43 | コメント (0) | トラックバック

2007年01月01日

IE で UTF-8 の含まれる XML が表示されなかったら

Simple FON Maps の XML を生成するときに、なぜか IE でエラーが出る場合がありました(日本語以外のマルチバイト文字が含まれる場合)。

テキストの内容に無効な文字が見つかりました。

IE で XML を表示しようとすると、このようなエラーが出ました。律儀なことに JavaScript 経由でもデータを読み込んでくれません。他のブラウザ Firefox, Opera では、何のエラーも出ずに読み込めたのですが…。

サポートページを調べてみると、出力データに言語仕様によって定義された有効な XML 文字範囲を超えた内容が含まれるようです。すなわち、制御文字などが混ざっているようです。

utf8::decode($str);
utf8::encode($str);

このように UTF-8 フラグを ON にしてから OFF にすれば解決しました。不要なバイナリを除去してるのだろうか…。そもそも、文字列を UTF-8 として扱うと決めているのであれば、フラグを ON にしておいた方がいいのかな。

とりあえず動くようになったからいいのですが、もう少し細かい部分を調べる必要がありそうです。

【関連記事】
Simple FON Maps を Google Earth に対応 (2006年12月31日)

【関連情報】
・PRB:下位文字が XML ドキュメントに含まれる場合、エラー メッセージ
 http://support.microsoft.com/kb/315580/ja

23:51 | コメント (0) | トラックバック

2006年12月17日

世界測地系の座標計算

Google Maps を使ったウェブアプリを書いていたので、その派生物(関数)を。

# IN: latitude(from), longitude(from), latitude(to), longitude(to)
# OUT: distance(meter)
sub dist {
    my ($lat_1, $lon_1, $lat_2, $lon_2) = @_;
    my $pi = atan2(1, 1) * 4;
    my $radius = 6378140;

    $lat_1 = $lat_1 * $pi / 180;
    $lon_1 = $lon_1 * $pi / 180;
    $lat_2 = $lat_2 * $pi / 180;
    $lon_2 = $lon_2 * $pi / 180;

    my $deg = sin($lat_1) * sin($lat_2) + cos($lat_1) * cos($lat_2) * cos($lon_2 - $lon_1);

    my $dist = $radius * (atan2(-$deg, sqrt(-$deg * $deg + 1)) + $pi / 2);

    return $dist;
}

2点の座標から距離を計算します。地球を真球と仮定して計算するので誤差が生じます。2点の座標があまりにも離れている場合は、その誤差が大きくなります。地球は、経線と赤道では赤道の方が長いです。

# IN: latitude(central), longitude(central), distance(meter)
# OUT: SouthWestLat, SouthWestLng, NorthEastlat, NorthEastLng
sub bounds {
    my ($lat, $lon, $dist) = @_;
    my $pi = atan2(1, 1) * 4;
    my $radius = 6378140;

    my $deg_lat = ($dist / $radius) * (180 / $pi);
    my $deg_lon = ($dist / $radius) * (180 / $pi) / cos($lat * $pi / 180);

    my $sWLat = $lat - $deg_lat;
    my $sWLng = $lon - $deg_lon;
    my $nELat = $lat + $deg_lat;
    my $nELng = $lon + $deg_lon;

    return $sWLat, $sWLng, $nELat, $nELng;
}

座標と距離から、その座標を囲む線(4点)を求めます。こちらも前記同様に誤差が出ます。

Google Maps API に getBounds という関数があり、この関数では getSouthWest と getNorthEast が取り出せ、それぞれから lat と lng を取り出すことが出来ます。この関数は、表示しているマップの端(南西と北西)の座標を取り出しているわけです。この関数を距離からエミュレートしたかったのです。

ラジアンなどをすっかり忘れていたおかげで、とても時間がかかりました…。そもそも正しいかどうかすら分からない。国内の座標を使う限りでは、それなりに正しい結果が出るようだけど。

後者は、なかなか便利なんだけどいかがでしょうか?多分 CPAN を探せば見つかるだろうけど。

23:16 | コメント (0) | トラックバック

2006年10月28日

携帯にメールを転送

僕が持っている携帯電話のキャリアは、最大手の DoCoMo です。スパムメールが届くのが嫌で、携帯電話からのメールしか届かないように設定してあります。

この設定には、少し盲点があります。携帯電話を対象とした ML を受け取れなくなるんですよね。

ML の設定方法には色々ありますが、大きく分けると「送信元を送信者にする」と「送信元を ML のアドレスにする」の2つに分けられるんじゃないかと。指定受信の設定を行っている場合、前者のメールが受け取ることができなくなります。

なぜ?

えっと。リレーサーバを挟んでしまうので、キャリア側が送信元偽装と判断してしまうのですよね。ん?分かりにくいか…。要は From を example@docomo.ne.jp にしておきながら smtp.example.com から送信されるのを防いでいるというわけ(指定受信設定の場合のみ)。

ということは、指定受信の設定で From ではなく、送信元 SMTP サーバを設定できればいいんだけど、普通な携帯利用者にはわかりっこない。ので、キャリア側がそんな機能を準備してくれるのは、絶望的なわけです。

ということで、オレオレゲートウェイを準備します。

オレオレゲートウェイの役割

1. mb@example.com でメールを受け取る
2. From を mb@example.com に書き換える(指定受信対策)
3. 携帯電話 mobile@example.jp に送信

ML の受信用に、外部にメールアドレスを準備するだけですね…。そして From が example@docomo.ne.jp などの場合に備えて、書き換えると。そうすれば、指定受信設定で mb@example.com を許可していれば、携帯で受信できる。

From を書き換えて送信するプログラム (mb_forward.pl)

CPAN モジュールを使えばもっと美しく書けるような気がしますが、標準インストールな環境で動くように心がけているので、一部は力技で。タイトルの抽出なんか、感動物ですよ。

外部プログラムの起動方法として procmail を利用します。別に .forward でも良いと思いますけど。

$ vi .procmail
:0 :
* for <mb@example\.com>
| /home/user/mb_forward.pl

レシピに To: mb@example.com などと書くのは時代遅れですね…。とか言ってみる。今回のように for <mb@example.com> などと書けば、送信者が BCC で送信しても、きちんと振り分けられます。

僕が .procmail を利用するのは、エイリアス設定で mb に相当する部分が、何であっても受信するようにしているからです。メールを受信して云々をいうプログラムを結構書くので、レシピは沢山あります。

example@docomo.ne.jp は、説明用のメールアドレスとして利用しました。もしかしたら実際に利用者がいる可能性もありますので、悪戯とかの無いようによろしく御願いいたします。

それでは、良き携帯 ML ライフをお過ごしください。

17:24 | コメント (4) | トラックバック

2006年06月26日

Google Maps で Lightbox JS を使いたい

画像をクールに表示する Lightbox JS という便利なスクリプトがあるのですが、ちょっと制約があって困っています。

<a href="images/image-1.jpg" rel="lightbox" title="my caption">image #1</a>

rel 属性を追加するだけで使用できるというシンプルな仕様なのですが、今回は、このシンプルな仕様がアダとなってしまいました。

Google Maps のふきだし内のリンクのように、リンクを JavaScript で生成している場合は、期待通りの動作が行われない。ページを読み込んだ後、リンクタグに rel 属性が付いているものに対して、下準備を行うからだ。

onclick="showLightbox(this); return false;"

ソースを斜め読みし、属性を追加する代わりに上記のように onClick を追加してみたのですが、いまいち上手く動かない。IE の場合は、最終的に Lightbox JS を使用しない場合と同じように表示されてしまう(return false; が効いていない?)。また、FireFox の場合は、半透明の背景画像が適用されない(背景画像がキャッシュされると大丈夫)。

どうやったら解決できるのかな?ソースを見ながら、使えるように移植するのがいいかなぁ。

【関連情報】
・Lightbox JS
 http://www.huddletogether.com/projects/lightbox/

19:30 | コメント (6) | トラックバック

2006年06月21日

BLOG 型画像ファイルの画像形式を変換する

Image::Magick を使えば、画像形式の変換が出来るのだけど、メモリ上で行う方法を調べてた。多くのサイトでは、物理ファイルを読み込む方法しか書いていない。

具体的には、予め画像のバイナリを MySQL の BLOB 型として保存しておき、表示するときに画像の形式を変換したかったのです。もちろん、必要に応じてリサイズも。

ということで、適当なコードを。

use Image::Magick;

# DB から画像ファイルのバイナリを取得
my $file = &get_blob();

my $image = Image::Magick->new();
$image->BlobToImage($file);

print "Content-Type: image/gif\r\n";
print "\r\n";
print $image->ImageToBlob(magick => 'gif');

get_blob にて BLOG 型の画像データを受け取って、それを GIF に変換するコードです。

BlobToImage も ImageToBlob も配列を扱うことが出来ます。なので、1日に1回画像形式を変換するといったバッチ処理が効率よく行えるのではないでしょうか。掲載したコードの最後の部分は、厳密にはリストです。

携帯の画像をストレージするようなサービスを始めるかもしれない。時間があればね。

【関連情報】
・Working with Blobs - PerlMagickリファレンス (Cepheid)
 http://www.ss.iij4u.or.jp/~somali/web/_perlmagick_ref.html#blobs

23:03 | コメント (0) | トラックバック

2006年06月18日

ブログの本文抽出にチャレンジ

zuzara.com を読んでいると、ブログの本文抽出にチャレンジしているのを見つけました。

tdかdivで囲まれた文字列で、文章と比べてHTMLのタグがあまり多くないもののうち、一番文字数が多いのが本文だろう、というアルゴリズム。

PHP で書かれたコードを Perl に移植しながら、もっと効率的なアルゴリズムが無いかを考えていました。

まずは、『タグの数』ではなく、比率をで判定するように改良(?)しました。

スコア = タグ除去後(length) / タグ除去前(length)

タグが含まれていないときが最大値になるので、スコアは 1 が最大となります。タグの数よりもこっちの方が良さそうだったのだけど、コメント部分を抽出してしまう可能性が非常に高い。だめぽ。

牛乳を飲みつつ考えていると(カルシウムを摂取して身長を伸ばす)、ひらめきましたよ!要は、長い文章を取り出せればいいのだから、句読点の多いブロックを取り出せばよくね?とね。

日本語の句読点がたくさん含まれる部分を本文とするプログラムを作ってみた。適当に作ったので、カウント部分がかなり手抜き。

句読点の数を見てブログの本文を抽出する (Perl)

それなりに上手くいってるかも。もう少し考えてみると、上部のブロックほどスコア(句読点の数)を高めにするようにしても良いかも。上部にあるものほど重要ということで。どちらかというと、コメントが抽出されないようにする感じ。

前から考えていたのは、こんなのが。

・直前のエントリと diff を取る
・RSS の description と比較する

どちらも定期的にクロールしていることが前提ですね。任意のページを渡したときに本文抽出してくれないのでいまいち。人間ってすごいよね。

今回は、判定するための文字列(ブロック)を zuzara.com に倣って『tdかdivで囲まれた文字列』としたのだけど、もっと良い方法が無いかな。ニュース記事のように、途中に table で画像を入れられるとどうしようもない。

【関連情報】
・ブログの記事本文を抽出するスクリプトをつくってみた
 http://blog.zuzara.com/2006/06/06/84/

20:41 | コメント (2) | トラックバック

2006年06月13日

PostgreSQL を使ってみた

諸事情で PostgreSQL を使いました。いつも使っている MySQL からの移植メモということで。

REPLACE tbl_name ( col_1, col_2 ) VALUES ( ?, ? )

REPLACE は PostgreSQL で使用できない。面倒がらずに INSERT と UPDATE を使い分ける。

INSERT tbl_name SET col_1 = ?, col_2 = ?

INSERT で SET を使うことは PostgreSQL では出来ない。これは UPDATE と合わせた形式で便利だったのだけど、素直に VALUES を使う。

・UNSIGNED が使えない
・AUTO_INCREMENT の代わりに SERIAL を使う

というあたりも。

今回は意識しなかったけど、内部的な値の問題で、ブール型 BOOLEAN は使わずに INT か CHAR を使う方が良いというのも。バイト数を考えれば、後者かな。

まとめると、標準化された仕様(SQL99)に従えということですな。ともあれ、良く使われる MySQL と PostgreSQL には、きちっと対応させたいと思う。コードがそのまま使えなくとも、最低限の変更で済むように。

標準化された仕様に従わないと DBI を使ううま味が無いかも。データベースエンジンを変更する機会はそうそう無いんだけれども。今回は、初めて使う PostgreSQL に対応させるために、まずは MySQL で構築したのが始まり。

移植にあたり Web+DB press (Vol.25) の特集を参照しました。データ型の対応表ね。

【関連情報】
・新しい業界標準「SQL99」詳細解説
 http://www.atmarkit.co.jp/fnetwork/tokusyuu/01sql99/sql99_0.html
・SQLデータ型一覧
 http://www.nslabs.jp/book2-sql-types.rhtml

03:19 | コメント (0) | トラックバック

2006年05月02日

Plagger::Plugin::CustomFeed::NewsCeekJp

日本の Perl 村では Plagger なるものが流行っているようです(海外でも?)。

Plagger は Bloglines to Gmail な Hack の進化したもので、すべてをプラグインで実装できるようにしたソフトウェアです。Blog ソフトウェアの Blosxom とか SMTP サーバの qpsmtpd とかを知ってる方は、それの RSS アグリゲータ版と思っていただけると話が早い。

データの入力と出力をプラグインで好きに変更できるソフトウェアという感じでしょうか。

Plagger::Plugin::CustomFeed::NewsCeekJp という CEEK.JP NEWS の入力を扱うプラグインがアップされていました。自分のサイトの関係物を他の人が作っていただけるのは、嬉しい限りです。

Plagger については、全然調べていないのですがこれを機会に、少し調べてみる。

ちなみに、色々な Blog Search で ceek を定点観測しているので、感想などを書いていただけると、概ね把握しています :) そういったことも考えると、エントリー宛ではなく、ブログ宛にトラックバックできるのも便利かもしれませんね。

【関連記事】
・Plagger::Plugin::CustomFeed::NewsCeekJp
 http://d.hatena.ne.jp/woremacx/20060502/1146522953
・Plagger をリリース
 http://blog.bulknews.net/mt/archives/001893.html

18:28 | コメント (0) | トラックバック

2006年04月15日

パフォーマンス監視 IE ツールバー

Visual C# で IE ツールバーにチャレンジしていたのだけど、やっとそれらしいものが出来上がりました。

パフォーマンス監視バー

CodeZine の記事を読みながらコーディングしたのですが、どうも trdgetNETSentBytePerSec あたりを Thread.Start するところで落ちるっぽい(無限ループがダメ?)。そんな最高の「ブラクラ」だったわけですが、よくよく考えたら、定期的に実行すれば良いのだから、タイマーの中にほりこんでやった。

private void timer_Tick(object sender, EventArgs e)
{
    float SpeedVal;

    // 通信速度取得と単位変換
    SpeedVal = Speed.NextValue() / 1024;

    // ラベルに表示
    speed_value.Text = SpeedVal.ToString("###0.00") + " Kbps";
    this.Refresh();
    System.Windows.Forms.Application.DoEvents();
}

こんな感じのコード。

PerformanceCounter を利用してネットワークの転送量を取りたく Network Interface というカテゴリを指定して、取得しようと思うわけですが、肝心のインスタンス名が分からない。

Broadcom 440x 10/100 Integrated Controller

がデバイス名なので、これを与えてみたけど、動かない。調べてみると、インスタンス名にスラッシュが含まれる場合は、アンダースコアに置換する必要があるらしい。

Broadcom 440x 10_100 Integrated Controller

よっしゃ、これで行ける!とか思ったらいけないでやんの。

Broadcom 440x 10_100 Integrated Controller - パケット スケジューラ ミニポート

これが正しかった…。分かるかボケ!インスタンス名の一覧を取得できるコードを書いて調べましたよ。

ということで、小さい一歩を踏み出しました。バーのバックカラーが IE と同じにならないし、縦幅が固定されない(デフォルトで見えないサイズ)し、いろいろ課題がたまってますが、少し進んだということで。

普通な検索バーを作ってみるかな。

【関連記事】
Visual C# 2005 Express Edition (2006年04月15日)

【関連情報】
・パフォーマンスカウンタのカテゴリ、カウンタの一覧を取得する
 http://dobon.net/vb/dotnet/system/performancecountergetcategories.html

18:12 | コメント (6) | トラックバック

2006年03月25日

HTML::Template 2.8 の不都合?

HTML::Template 2.8 をもう少し調べていたところ、不都合っぽいのを発見しました。

TMPL_VAR の ESCAPE の初期設定が 0 で固定だったのが、変更できるようになったということですね。

ということを書いたときは、次のように理解していたのです。

HTML::Template->new(default_escape => 'HTML');

こう書けば、デフォルトで以下が HTML としてエスケープされます。

<TMPL_VAR NAME="KEY">

そして、以下のように記述するとエスケープされないと。

<TMPL_VAR NAME="KEY" ESCAPE="">

この理解は間違いで、最後のエスケープ回避(アンエスケープ)が出来ませんでした。実際に HTML::Template のソースコードを読んだのですが、そんな処理が無かった…。というか、無理やり default_escape が突っ込まれた感じで、あまり美しくなかった。

ちなみに、こんな不都合も。

<TMPL_VAR NAME="KEY" ESCAPE="JS">

これは、有効になりますが、以下は有効になりません。

<TMPL_VAR NAME="KEY" ESCAPE='JS'>

" でも ' でもいいような感じで書かれていたのですが、なぜか JS だけはダメです。というか、その辺の抽出がハードコード(?)されていたので、書き忘れなんじゃないかと。

自分の使いやすいようにカスタマイズして、別のモジュールにしたほうがいいかもしれないと思ってきましたよ。結構使われているモジュールでも、ソースコードが美しいというわけじゃないんだね。機能が美しいから使われるわけであって、ソースコードの良し悪しは関係ないと再確認。といいつつも、この HTML::Template は、自身の利用実態に微妙に合わない…。

こんなことを書きながら調べていたら、メーリングリストにパッチが出ていました。

【関連記事】
HTML::Template 2.6 と 2.8 の違い (2006年03月05日)

【関連情報】
・default_escape patches (html-template-users)
 http://sourceforge.net/mailarchive/forum.php?thread_id=9948445&forum_id=9830

23:22 | コメント (0) | トラックバック

2006年03月11日

Google Maps で連続する動作が上手くいかない

位置情報を使ったサービスを作りたくって Google Maps API をいじりながらゴニョゴニョしているのだけど、どうも上手くいかない。かれこれ、8時間ほど費やしたような気がする。

座標 A から座標 B にスクロール移動(滑らかに移動)させたいというだけなんだけど。

recenterOrPanToLatLng(latLng)

という関数を使えば、スクロール移動させることができます。

Centers the map at the given point, doing a fluid pan to the point if it is within the current map viewport.

という通り、表示域を超えるとスクロール移動せずに、ページが切り替わるような感じになってしまう。これを回避するような関数を作成したいわけです。

アプローチは、2通り思いつきました。

1. 少しずつ移動

表示域で動ける最大の座標に移動しながら、数回にわたりスクロール移動を繰り返す方法。

2. 縮尺を変更して移動

移動先の座標が表示域に含まれるように縮図を変更してから移動。移動後に元の縮尺に戻す。

双方の関数を実装してみたのだけど、どうも連続した動作ができないような気がする。例えば recenterOrPanToLatLng を続けて呼び出すと、最後の移動しかスクロールされません。ループ処理をしている場合、スクロール移動処理 recenterOrPanToLatLng が終わってから次の動作に進むことを期待しているのに…

どんな座標にでもスクロール移動させるという需要はあると思うのだけど、誰もそんな関数は作っていないのかな?他のアプローチでも良いので参考になるページを探し中。

Yahoo! Maps Web Services が日本に対応していたら、そっちも検討するんだけどなぁ。

【関連情報】
・Google Maps API Documentation
 http://www.google.co.jp/apis/maps/documentation/

22:28 | コメント (0) | トラックバック

2006年03月05日

HTML::Template 2.6 と 2.8 の違い

HTML::Template を使う機会が増えてきたのですが、日本語ドキュメントのバージョンが少し古くて残念。ということで、ドキュメントの diff を取って、何が追加されたのかを調べてみた。

There is also the "ESCAPE=JS" option which may be used for VARs that need to be placed within a Javascript string. All \n, \r, ' and " characters are escaped.

TMPL_VAR に ESCAPE=JS というオプションが追加されています。これを使えば、JavaScriptのコードに応じたエスケープを行ってくれるようです。

NOTE: global_vars is not global_loops (which does not exist). That means that loops you declare at one scope are not available inside other loops even when global_vars is on.

global_vars の振る舞いの補足がなされています。あるループ内で、そのループを使用する事はできませんよ。ということ。使えたほうが便利なような気もするので、そういうオプション global_loops が欲しいところですね。(global_loops なんて無いよ!という説明をしてるんだけど…)

default_escape - Set this parameter to "HTML", "URL" or "JS" and HTML::Template will apply the specified escaping to all variables unless they declare a different escape in the template.

default_escape というオプションが追加されています。このオプションには「HTML」「URL」「JS」を設定することができ、それらを設定した場合は TEML_VAR で ESCAPE の設定を変更しない限り、それらのエスケープ処理が行われます。 TMPL_VAR の ESCAPE の初期設定が 0 で固定だったのが、変更できるようになったということですね。

後は、謝辞の項目に Paul Baker, Gabor Szabo, Craig Manley の3名が追加されていました。

変更点には、サンプルコードの修正がありました。というか、単純にタグの閉じ忘れなんだけど…

【関連情報】
・HTML::Template - CGI スクリプトから HTML テンプレートを使うための Perl モジュール
 http://perldoc.jp/docs/modules/HTML-Template-2.6/HTML/Template.pod
・HTMLTemplate - Perl module to use HTML Templates from CGI scripts
 http://search.cpan.org/~samtregar/HTML-Template-2.8/Template.pm
・HTML::Template Changes
 http://search.cpan.org/src/SAMTREGAR/HTML-Template-2.8/Changes

19:59 | コメント (1) | トラックバック

2005年11月16日

MySQL でランダム表示

MySQL のレコード取り出しの際に、ソートをランダムにできないものかと調べていたら、あった。

SELECT * FROM table ORDER BY RAND()

こんな感じで。ソートを RAND にすれば良いという…

使い道としては、広告を表示したり、クイズを表示したりに使えるんじゃないかと思う。というか、そういう使い方をしたくて調べてた。

MySQL って深いなぁ。他にも面白い関数がありそうだ。

22:11 | コメント (0) | トラックバック

2005年11月12日

黒と白を決めるサブルーチン

Ads by Goooooogle の色

Google AdSense の代替広告用の CGI を作っています。データは Amazon Web Services を使うことになると思うのですが、デザインを Google AdSense に似せてみようと思います。

Google AdSense は、枠線のカラーを決めると、自動的に Ads by Goooooogle のカラーを #000000 か #FFFFFF に決めてくれます。これは、どういう仕組みで判定しているのだろうかと、調べてみた。

結構、簡単な仕組みでした。

明るさ=R×0.30+G×0.59+B×0.11

この式で明るさが決まるらしい。明るさは、与えられる RGB の範囲で決まりますが、大概は 0〜255 だと思います。

Perl にて、サブルーチンも作成。

sub get_powered_link_color {
    my ($border_color) = @_;
    my ($powered_link_color);

    if ($border_color =~ /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/) {
        my ($r, $g, $b) = (hex $1, hex $2, hex $3);

        if ($r * 0.3 + $g * 0.59 + $b * 0.11 < 127) {
            $powered_link_color = '#FFFFFF';
        } else {
            $powered_link_color = '#000000';
        }
    }

    return $powered_link_color;
}

与える引数は、枠線色のRGB値(ex. #74F17A)です。返り値は、テキスト色に使ったらよいRGB値です。とはいっても、2色しかないんですが。引数の値がおかしいと、返り値は undef となります。

hex を使って、16進数の値を10進数に変換しています。

ついでに、補色を取得するサブルーチンも作ってみた。

sub get_complementary_color {
    my ($before_color) = @_;
    my ($after_color);

    if ($before_color =~ /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/) {
        my ($r, $g, $b) = (hex $1, hex $2, hex $3);

        $after_color = sprintf "#%02lx%02lx%02lx", 255 - $r, 255 - $g, 255 - $b;
    }

    return $after_color;
}

補色っていうのは、ある色の対極にある色です。混ぜると、白色になる色の組み合わせ(絵の具だと灰色かな)が、補色の関係にあります。補色を知ると、どんないいことがあるのか?ということですが、基本的に、背景色の補色をテキストカラーにすると、そのテキストが目立ちます。

自分専用の Amazon Search を作れないかと奮闘中。後ほど、ソースの公開や、サービスとして提供できればいいなと妄想中。

【関連記事】
Google AdSense を導入しました (2005年11月10日)

【関連情報】
・モノクロ(単一色)階調表現への変換
 http://www.sm.rim.or.jp/~shishido/monog.html

22:13 | コメント (1) | トラックバック

2005年08月09日

Perl レベルは 6 くらい

Perlプログラミング救命病棟 という書籍の中に、Perl のプログラマも指標をレベル別に書いているらしい。

自分に当てはめてみると、大体、6に成り立てくらいだと思う。結構贔屓目に評価ですが。もちろん、少し先取りしてみるとか、取りこぼししているとかはあるのだけど。

自分が、 Perl プログラマとして優れていないと考えているのは、以下の2点が大きい。

・オブジェクト指向を理解していない
・CPAN をあんまし使わない

オブジェクト指向を理解していないのが大きい。自分専用のライブラリは、どんどん作っているのだけど、それをモジュールに書き換えることができない。不便を感じてきたのでお勉強中。他人のコードは、やっぱり勉強になりますね。

あとは、 CPAN をあまり使わない。なんつーか、「車輪の再開発」が楽しすぎる。要は、仕組みを理解して、実際にコードに移し、それが想定どおりに動くのがうれしい。アマチュアプログラマーってそんなもんじゃない?

CPAN を多用しないなんて Perl の便利さの 6割 くらいは捨ててる感じだ。

CGI.pm を駆使してWeb ベースのアプリケーションを作成する

CGI.pm とか使ったこと無いorz

可読性の低いコードを量産しているわけですが、自分ひとりで開発している独りよがり根性であれば、余り問題にならないわけで。と、問題を感じないから、次のステップに進めないのも事実。

とりあえず、オブジェクト指向を理解して、次のステップに進もうと思う次第です。

今まで買った、Perl 関連の書籍って、 CGI&PerlポケットリファレンスBlog Hacks をはじめとした Hacks シリーズ しか持っていないのですが(つーか Hacks シリーズは、本棚の肥やしになってるだけかも)、この Perlプログラミング救命病棟 も面白そうなので、購入を検討してみよう。

【関連情報】
・Perlプログラマのレベル10 - Perlプログラミング救命病棟より (naoyaのはてなダイアリー)
 http://d.hatena.ne.jp/naoya/20050809/1123563794
・Perlオブジェクト指向プログラミング
 http://www.pure.ne.jp/~learner/program/Perl_oo.html

16:25 | コメント (0) | トラックバック

2005年08月06日

ブラウザのデフォルトスタイルを消去

最近は、テーブルレイアウトからの脱却を図るために CSS でのデザインを心がけています。本来の HTML に回帰するわけだ。別に原理主義者では無いけどね。

HTML をきちんとマークアップ言語に徹することは、テキストブラウザや読み上げ(音声)ブラウザにもやさしいと思っています。反面、デザインを CSS に頼るのだけど、ブラウザ間の差異が、自分の意図したデザインを吐き出してくれなくてイライラする。

CSS の解釈の違いは、仕方ないとしても、少なくとも HTML のタグの解釈くらいは、初期化した方が良いだろう。と思っていたら、あった。

* {
    margin: 0;
    padding: 0;
    font-style: normal;
    font-weight: normal;
}

いわれると当たり前。ちうか CSS でアスタリスク(*)が使えるなんて知らなかった。ちょっと前は、 Apache の NameVirtualHost で * が使えることを知ったばかりだし。結構な範囲で * が使えるんじゃないかという気がしてきた。

Perl と同じく CSS もはまる。 Perl は、ずっと使っているので非効率な解決方法が思い浮かぶ限り何でも解決できるのだけど、慣れていない CSS は、その非効率な解決方法すら思い浮かばない。リファレンスくらいは持っておいてもいいかなぁ。

【関連記事】
CentOS 設定中 (2005年07月09日)

【関連情報】
・CSS を作成する際のお約束 (Lucky bag::blog:)
 http://www.lucky-bag.com/archives/2005/08/default_style.html

17:38 | コメント (0) | トラックバック

2005年07月16日

プロセス管理プログラム

CEEK.JP NEWS は、2年くらい前に作ったプログラムで動いています。今もそうだけど、2年前は、かなりへっぽこなプログラマだったので、ソースもぐちゃぐちゃです。というか、もう読みたくないようなコードですね。

常駐プログラムじゃないのに、以下が宣言されていない始末。

use strict;

まぁ。それとはあまり関係ないんだけど、正規表現が多いし、エラー処理が適当なおかげで、プロセスがずっと立ち上がったままというのが多々あるわけです。どこかで無限ループしているような気がするのだけど、原因を確かめる気にすらならんorz

ということで、暴走しているっぽいプロセスがあったら、勝手に kill すうようなプログラムを作成しました。

プロセス管理プログラム(Perl)

kill の部分をメール送信にするのもいいかもしれない。

$ps = `ps aux | grep perl`;

この grep で、監視するプログラムを調整します。ユーザー名も指定しておく方がいいです。だって kill できないもん。

$max = 5;

この時間(分)を過ぎたプロセスを抽出するわけですな。分単位で書くのだけど、あまり長く設定すると無理。日付をまたぐ処理がかなり適当なので。

あとは、このプログラムを Cron で定期的に実行すれば監視できます。が、このプログラムが立ち上がらないほどサーバに負荷がかかっている場合は、無理です。諦めてください。

# Start Time の部分は要らないような気がしてきた。 # Ps Time だけをチェックする方がよさげ。ま。せっかく書いたので、消さないでそのまま。

自己責任でどうぞ。

そういえば、リモートで物理的にリセットボタンを押す装置が欲しいな。前にも書いたっけ?

23:15 | コメント (7) | トラックバック

2005年06月23日

mt-com.cgi からのリダイレクト

以前、スパム対策に mt-comment.cgi を mt-com.cgi にリネームするという方法を採用していたなおですが、再インストールで元のファイル名に戻してしまいました。

アクセスログを見ていると、いまだに Google は mt-com.cgi となっていたので、リダイレクトすることにしました。

はじめは、普通に .htaccess を使おうと思ったのだけど、これだとコメントのページにたどり着けない。ということで、わざわざ mt-com.cgi という CGI を作成しました。

#!/usr/bin/perl

use strict;

my $id;

foreach (split(/&/, $ENV{'QUERY_STRING'})) {
    my ($name, $value) = split(/=/);
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    if ($name eq 'entry_id') {
        $id = $value;
        $id =~ s/\D//g;
        last;
    }
}

print "Status: 301 Moved Permanently\n";

if ($id) {
    printf "Location: http://hostname/archives/%06d.html\n\n", $id;
} else {
    print "Location: http://hostname/\n\n";
}

exit;

普通にリダイレクトしてるだけだから、エントリーを削除とかやってるとダメです。まぁ。Google のロボット対策みたいなもんですね。

22:15 | コメント (0) | トラックバック

2005年05月20日

Encode と Unicode::Japanese (2)

Encode と Unicode::Japanese を過去に取り上げたのだけど、また調べてみた。というか、軽くベンチマークを取ってみた。

UTF-8 -> Shift_JIS -> EUC-JP -> UTF-8 -> ...
の変換を、1000回行ったときの time を取ってみました。結構適当なので、参考程度に。

Encode

real    0m0.352s
user    0m0.320s
sys     0m0.010s

Unicode::Japanese

real    0m1.189s
user    0m1.150s
sys     0m0.000s

結果から言うと Encode の方がいいのだけど、僕は、一部で Unicode::Japanese を使うことにした。

UTF-8 -> EUC-JP の変換が、標準モジュールである Encode を使うと、うまくできない。「〜」が変換できずに ? に置き換わってしまうんだよね。逆を試しても変な感じ。というか UTF-8 が絡むとダメだと思う。

Unicode::Japanese を使うと UTF-8 -> EUC-JP がうまく変換できていい感じ。バックグラウンドで動くクローラーは、全部 Unicode::Japanese を使おうかな。バックグラウンドに限らず使う方が良いかもしれない。

文字コードの問題は、悩める。

【関連記事】
Encode と Unicode::Japanese (2005年01月08日)

01:12 | コメント (0) | トラックバック

2005年05月17日

全角英数字 ⇔ 半角英数字

全角英数字を半角英数字に変換するときは、どのような方法を使っていますか?僕は jcode.pl をずっと使っていました。というか jcode.pl は、手放せないライブラリだったわけです。

&jcode::tr(\$text, '0-9A-Za-z', '0-9A-Za-z');

しかし、常々、正規表現で書くことは出来ないのかと考えていたわけです。書きました。

Perl メモ より

$a = qr{(?<!\x8F)};
$b = qr{(?=(?:[\xA1-\xFE][\xA1-\xFE])*(?:[\x00-\x7F\x8E\x8F]|\z))}x;

全角英数字 → 半角英数字

$text =~ s/$a\xA3([\xB0-\xB9\xC1-\xDA\xE1-\xFA])$b/pack("C", ord($1) - 0x80)/oeg;

半角英数字 → 全角英数字

$text =~ s/([0-9A-Za-z])/pack("C*", 0xA3, ord($1) + 0x80)/eg;

Perl で pack や ord を使う機会がほとんど無いけど、今回のために調べたり。というか、自分がコードを書くときに使ったのは、これがはじめて。

もっといい方法は無いかな。

【関連情報】
・Perlメモ
 http://www.din.or.jp/~ohzaki/perl.htm

22:26 | コメント (4) | トラックバック

EUC の正規表現

最近は、ニュース検索に新たな機能を追加しようと試行錯誤しています。

EUC の文字列が文字化けしていないかどうかをチェックする必要がありました。チェック自体は難しくなく、単に EUC の範囲外の文字が含まれているかどうかを調べればよいのです。

EUC の 1byte 文字は、以下のように表現することが出来ます。というか、すべての ASCII 文字 はこれ。

[\x00-\x7F]

しかし、よくよく考えると、この表現には制御文字も含まれているんですよね。なので、このままマッチさせると文字化けを発見することが出来ないわけです。なので、制御文字を除いてマッチさせるのが吉だと思う。

[\x20-\x7E]

ということで、僕は、以下のように定義して文字化けが無いかどうかを調べています。

$euc = '[\x20-\x7E]|[\x8E\xA1-\xFE][\xA1-\xFE]|\x8F[\xA1-\xFE][\xA1-\xFE]';
if ($text !~ /^(?:$euc)+$/o) {
    print "化けてるよ!";
}

もっといい方法が無いものだろうか。

22:12 | コメント (0) | トラックバック

2005年05月06日

SpeedyCGI では alarm が使えない?

CEEK.JP の search.cgi を SpeedyCGI で動かしたいのだけど、うまくいきません。調べてみたところ、その原因のひとつは SpeedyCGI が alarm に対応していないのかも。

In start_perl, use a poll() timeout instead of an alarm to implement the timeout while waiting for an accept. It's cleaner than a signal.

実際、簡単なコードで試してみましたが、期待した結果を得ることは出来ませんでした。

しかし、もうちょっと調べてみたら、バージョン 2.2 で修正されているようです。

- Fixed bug where alarm's were unusable from within perl code.
- Signal handling in the backend has been cleaned up. Signal settings will no longer be altered between perl runs.

なーんだ。僕の使ってるバージョンが古いんじゃん。

ちなみに SpeedyCGI は、2ちゃんねるの bbs.cgi でも使われています。というか、その作業スレッドで知ったんだけどね。なので、2ちゃんねるの SpeedyCGI も古いバージョンなのではないかと。

【関連情報】
・bbs.cgi再開発プロジェクト5 (2ちゃんねる)
 http://qb5.2ch.net/operate/kako/1103/11034/1103495887.html
・Revision history for Perl extension SpeedyCGI.
 http://daemoninc.com/SpeedyCGI/CGI-SpeedyCGI/Changes

19:17 | コメント (0) | トラックバック

もっと古いエントリー

wget 風味にファイルを保存 (2005年03月14日)
.htaccess に自動追加 (2005年03月07日)
alarm と time (2005年01月16日)
スタイルシートと整理 (2005年01月13日)
Encode と Unicode::Japanese (2005年01月08日)
こんな間違いしちゃダメ (2005年01月05日)
配列に入ったアドレスをばらばらに (2005年01月04日)
ULとスタイルシート (2004年11月29日)
最近よくやるミスなど (2004年11月22日)
Namazu を CGI から呼び出す (2004年11月12日)
MySQL + Namazu の原始的手法 (2004年11月05日)
ISBN のチェックサム (2004年11月04日)
マッチ演算子 or index (2004年11月02日)
RSS Robot (2) (2004年10月30日)
RSS Robot (2004年10月29日)
Perl での RSS 処理ベンチ (2004年07月11日)
PageRank チェックサムアルゴリズム (2004年06月29日)