文房清玩の最近のブログ記事

Pushkin Concordance C++ Source Code

Конкорданс к тексту А. С. Пушкина プーシキン見出語コンコーダンスを実現する C++ プログラムのソースコードを GNU Global で整形して Web に公開した。Pushkin Concordance C++ Source Code から参照できる。

Boost.Interprocess による共有メモリ操作や,Boost.Regex,Boost.Locale による UTF-8 文字列の正規表現・文字操作など,このコンコーダンス・ソフトウェアの作成過程でいろいろ勉強した要素は,今後も役立ちそうである。GNU Global によって,データ構造や関数のクロス・リファレンスが容易になり,そういう要素を探索しやすくした。

20120517-global.pngGNU Global Source Code Tour
掲載したのは以下の 3 プログラム。
  1. CorpusLoader コーパス共有メモリローダ。プーシキンのコーパステキストデータ,作品名データベースを読み込み,共有メモリに格納し,他プロセスからアクセスするためのアドレスを提供する。
  2. WordTreeBuilder 単語二分木共有メモリビルダ。コーパステキストを読み,単語切り出し,見出語変換を行い,単語(キー)/出現回数/出現位置情報からなる単語二分木データベースを共有メモリ上に構築する。見出語形,出現形の 2 種類の二分木を生成し,その入口アドレスを他プロセスからのアクセスのために提供する。
  3. concordance コンコーダンス出力 Web サーバ。コーパスローダ,単語二分木ビルダが共有メモリに構築したデータベースをユーザに提供する。Wt C++ Web Toolkit を使用。

プーシキン全集の Web ページからコンコーダンス用コーパスを作成するプログラムについては,Perl やシェルスクリプトであるということもあり(Global が未対応),掲載していない。コーパス作成についても,いずれ — 何をやったのか忘れないうちに — 整理しておきたいと思う。

* * *

高嶋みどり作品集 CD を入手した。高嶋みどりは主に合唱曲で有名な作曲家である。彼女の合唱曲は,中学・高校合唱部の生徒からプロの合唱団員に至るまで,定番作品として愛されている。

『白鳥 — アポリネールの詩による四つの無伴奏混声合唱曲集』が聴きたくて,この CD を注文した。その他,谷川俊太郎詩による『若者たちの悲歌』,草野心平詩による『青いメッセージ』が収録されている。

全国高校合唱コンクールで聴いた,『白鳥』1曲目の『露営のともしび』がとくに好きである。オーケストラもよいが,無伴奏の合唱曲にたまに浸ると,心が洗われる神妙な気分になる。

日本合唱曲全集 白鳥/高嶋みどり 作品集 2
松原混声合唱団, 湘南市民コール, 湘南市民コール
日本伝統文化振興財団 (2011-03-30)

プーシキン全集 Web コンコーダンス・プログラムの Web インタフェース部分は Wt C++ Web Toolkit(以下 Wt)を使って拵えた。この C++ ライブラリは,Widget(画面部品オブジェクト)ベースで Web 画面を構成しつつ,ビジネスロジックも C++ で書いて行くスタイルを想定したものである。今日はコンコーダンス・プログラムで覚えた Wt のサワリ(奥が深いのでサワリに過ぎない)についてメモを残しておく。

最近では,Web のサーブレットを Java で作成し,ビジネスロジック(昔からある基幹システムは多く C/C++ で書かれている)とは Java JNI や CORBA,RPC,SOAP で接続し,画面周りは JSP ないしは HTML + JavaScript ベースで準備するのが一般的のように思われる。こうすることで,Web デザインとビジネスロジックを分割して,前者を Web デザイナ,後者をプログラマが担当するというふうに,それぞれのプロフェショナルに作業分担を行うのがやりやすい,というメリットがある。私の担当システムでもこのやり方が主流である。

この潮流からすると,Wt のデザインパターンは一見,時代に逆行しているように私には思われた。でも実際にこれでプログラムを書いてみると,確かに HTML ほど画面構築は簡単ではないが,ヘタにデザイナに弄らせるとまずい Web フォームのような基本インタフェースを含め,コンテンツ HTML の枠組みマークアップ部分をシステム屋・プログラマに任せつつ,「見た目」のデザイン自体はスタイルシートとしてデザイナに任せ,説明文など文字コンテンツについては外部 XML の編集という形で運用者・(プログラマでない)プロジェクト要員に分担させることができる,と納得した。Wt は,総じて,極めてよく考えられたプラットフォームであることがわかったのである。いまや Web デザインは HTML ではなく CSS でこそ実現する時代なのである。

既存ソフトウェア資産は C/C++ が多いし,Java プログラマよりも C/C++ プログラマのほうが圧倒的に人材が豊富なので,プロジェクトの運営もしやすいと私は思う。Wt なら C++ であらゆる Web コードが実現でき,比較的高速に動作するし,C/C++ の膨大な既存資産との連携に関しても,Java よりもずっと悩みが少ない。しかも,画面 HTML,JavaScript をまったく書かずに Ajax 非同期通信の高速 Web サーバ・アプリケーションを構築できる。また,システムの作り・ビジネスロジックを完全に隠蔽することができる(どんなスタイルシートを使っているのかすらユーザにはわからないし,リンクすら見えず画像をパクられることも回避できる。システムが生成した画面の HTML から見えるのはシステムが動的に生成・送信した,わけのわからない JavaScript ばかりである)。「何でいまさら Web アプリを C++ で?」という方はぜひお試しいただきたいと思う。

Wt ライブラリのインストールについてはブログ記事 Wt C++ Web Toolkit にしるしたので,そちらを参照。ただし,その記事には FastCGI について記述がなかったので,もし Wt で作成した Web サーバプログラムを FastCGI で動作させたいのであれば,Wt インストールに先立って,FastCGI のデベロッパライブラリと Apache モジュールをシステムに組込んでおかなければならない。これらは FastCGI のサイトのダウンロードページから,それぞれ,fcgi-current.tar.gzmod_fastcgi-current.tar.gz を取得して,インストールする。./configure; gmake; sudo gmake install で導入はしごく簡単である。その上で Wt を make すると,FastCGI 用プログラムを作成するための Wt ライブラリ libwtfcgi.so(Mac OS X の場合は libwtfcgi.dylib)がコンパイルされるはずである。

Wt ベースのコンコーダンス Web アプリは以下のようなものである。ただし,Wt に関係の薄いコンコーダンス・ビジネスロジック部分は省略してある。

/* -*- coding: utf-8; mode: c++; -*-
 * Concordance to A. S. Pushkin's Works
 * $Id: concordance.cpp 78 2012-05-05 18:58:17Z isao $
 * Copyright (C) 2012, isao yasuda
 */
 
#include <Wt/WApplication>
#include <Wt/WContainerWidget>
#include <Wt/WEnvironment>
#include <Wt/WLineEdit>
#include <Wt/WGroupBox>
#include <Wt/WButtonGroup>
#include <Wt/WRadioButton>
#include <Wt/WPushButton>
#include <Wt/WCheckBox>
#include <Wt/WText>
#include <Wt/WBreak>
#include <turglem/lemmatizer.hpp>
#include <turglem/russian/charset_adapters.hpp>
#include <boost/tokenizer.hpp>
#include <boost/locale.hpp>
#include "ShmCorpusWordTree.hpp"
 
using namespace Wt;
using namespace boost::interprocess;
typedef std::pair<std::string, std::string> string_type;
 
/* コンコーダンス用グローバル変数,関数の記述。省略 */
 
// コンコーダンスクラス
class Concordance : public WApplication
{
    WLineEdit*    expEdit;
    WButtonGroup* wbg0;
    WButtonGroup* wbg1;
    WGroupBox*    gbox;
    WCheckBox    *g0, *g1, *g2, *g3, *g4, *g5, *g6, *g7,
        *g8, *g9, *g10, *g11, *g12; // 00-11 + all
    WText* wmesg;
    WText* pmesg;
    WText* cmesg;
    void clearvse();
    void clearother();
    void generate();
    void errorsend(const std::string emsg);
public:
    Concordance(const WEnvironment& env);
};
 
Concordance::Concordance(const WEnvironment& env)
    : WApplication(env)
{
    // メッセージファイル message.xml の指定
    messageResourceBundle().use(appRoot() + "message");
    // タイトル
    setTitle(L"Динамический Конкорданс к сочинениям А. С. Пушкина");
    // スタイルシート
    useStyleSheet("style.css");
    // message.xml から header を出力
    root()->addWidget(new WText(WString::tr("header"), XHTMLText));
    root()->addWidget(new WText(WString::tr("subheader"), XHTMLText));
    root()->addWidget(new WBreak());
 
    // 入力プロンプト(初期値付き))
    root()->addWidget(new WText(L"Выражения ", XHTMLText));
    // expEdit = new WLineEdit(L"^П[ЕЁ]СТР.*", root());
    // 入力エリア: Enter key で submit 
    expEdit = new WLineEdit(root());
    expEdit->setEmptyText
        (L"Вводите Слова или Регулярные выражения, напр.: п[её]стр.*");
    expEdit->setTextSize(70);
    // expEdit->setFocus(); // focus より説明テキスト
    expEdit->enterPressed().connect(this, &Concordance::generate);
    // submit ボタン
    WPushButton *button = new WPushButton(L"поиск", root());
    button->setMargin(10, Left);
    // ジャンル一覧へのリンク(message.xml)
    root()->addWidget(new WText(WString::tr("link"), XHTMLText));
    root()->addWidget(new WBreak());
 
    // 見出語形/出現形選択ラジオボタン
    WGroupBox *gbox0 = new WGroupBox(L"Форма Базы данных", root());
    gbox0->setId("gbx0");
    wbg0 = new WButtonGroup(gbox0);
    WRadioButton *rb0;
    rb0 = new WRadioButton(L"Lemmatized (форма заглавных слов)", gbox0);
    wbg0->addButton(rb0, 0);
    rb0 = new WRadioButton(L"Non Lemmatized (форма в тексте)", gbox0);
    wbg0->addButton(rb0, 1);
    wbg0->setCheckedButton(wbg0->button(0)); // Lemmatized
 
    // 入力を見出語変換(見出語形の場合のみ)
    WGroupBox *gbox1 = new WGroupBox
        (L"Лемматизация входа", root());
    gbox1->setId("gbx1");
    wbg1 = new WButtonGroup(gbox1);
    WRadioButton *rb1;
    rb1 = new WRadioButton(L"ON (только в случае Lemmatized)", gbox1);
    wbg1->addButton(rb1, 0);
    rb1 = new WRadioButton(L"OFF", gbox1);
    wbg1->addButton(rb1, 1);
    wbg1->setCheckedButton(wbg1->button(0)); // YES
 
    // ジャンル選択チェックボックス
    gbox = new WGroupBox(L"Выбор жанров", root());
    gbox->setId("gbx");
    g12 = new WCheckBox(L"Все", gbox);
    g0  = new WCheckBox(L"Стихи", gbox);
    g1  = new WCheckBox(L"Поэмы", gbox);
    g2  = new WCheckBox(L"Сказки", gbox);
    // その他チェックボックス設定は省略
    // ジャンルデフォルト: すべて
    g12->setChecked(true);
    // チェック時イベント処理登録
    g0->checked().connect(this,  &Concordance::clearvse);
    g1->checked().connect(this,  &Concordance::clearvse);
    g2->checked().connect(this,  &Concordance::clearvse);
    // その他チェックボックス設定は省略
    g12->checked().connect(this, &Concordance::clearother);
 
    // ラベル表示,ユーザ入力内容表示エリア
    wmesg = new WText(root());
    pmesg = new WText(root());
    root()->addWidget(new WBreak());
 
    // コンコーダンス出力エリア
    cmesg = new WText(root());
    root()->addWidget(new WBreak());
 
    // フッタを message.xml から出力
    root()->addWidget(new WText(WString::tr("footer"), XHTMLText));
    root()->addWidget(new WBreak());
 
    // 入力内容で submit
    button->clicked().connect(this, &Concordance::generate);
}
 
// エラーメッセージ出力
void Concordance::errorsend(const std::string emsg)
{
    WString s(emsg, UTF8);
    s = "<p class=\"err\">" + s + "</p>";
    cmesg->setTextFormat(XHTMLUnsafeText);
    cmesg->setText(s);
}
 
// オール以外クリア
void Concordance::clearother()
{
    // オール指定がチェックされていたらほかをクリア
    if (g12->isChecked()) {
        g0->setChecked(false); g1->setChecked(false); g2->setChecked(false);
        // その他設定省略
    }
}
 
// オール指定クリア
void Concordance::clearvse()
{
    // オール指定以外がチェックされていたらオール指定をクリア
    if (g0->isChecked())  g12->setChecked(false);
    if (g1->isChecked())  g12->setChecked(false);
    // その他設定省略
}
 
// コンコーダンス生成
void Concordance::generate()
{
    const WEnvironment& env = WApplication::instance()->environment();
    // ユーザ入力単語式: WT::WString を std::string に toUTF() で変換
    std::string ldata = (expEdit->text()).toUTF8(); // 元入力
    std::string userip = "IP: " + env.clientAddress() + " Req: " + ldata;
    logging(userip.c_str(), 0);
 
    // 大文字変換
    std::string udata = boost::locale::to_upper(ldata);
 
    // 実行 Word Tree の選択,Lemmatizer 実行可否判断
    offset_ptr<tree> word_tree; // 実行 Word Tree
    WString opts(ldata, UTF8);
    opts = L" (Ваш вход: " + opts;
    // Lem Tree on なら見出語形ツリー選択
    if (wbg0->checkedId() == 0) {
        word_tree = word_tree_l; // select Lemmatized 
        opts += L"; DB: Lemmatized; Лемматизация входа: ON)";
        // Lemmatizing on なら Lemmatizer で入力を見出語変換する
        if (wbg1->checkedId() == 0) {
            lemmatizer(udata);   // 見出語変換
        }
    } else {
        // off なら出現形ツリー選択
        word_tree = word_tree_a; // select Non Lemmatized
        opts += L"; DB: Non Lemmatized; Лемматизация входа: OFF)";
    }
 
    // ユーザ入力をトークナイザで分解し,式を vector にセット
    std::vector<std::string> wds;
    exp_tokenizer(udata, wds);
    if (wds.empty()) {
        errorsend("No expressions specified.");
        return;
    }
 
    // 対象ジャンルのセット: global int vector にセット
    // - g0 から順にチェックを確認し,true なら push_back
    // - 全選択がチェックされている場合は vector を空にする
    // - ひとつも選択がない場合は全選択となる
    genrev.clear();
    if (g0->isChecked())  genrev.push_back(0);
    if (g1->isChecked())  genrev.push_back(1);
    if (g2->isChecked())  genrev.push_back(2);
    // 中略
    if (g12->isChecked()) genrev.clear();
    if (genrev.empty())
        grall = true;
    else
        grall = false;
 
    // ラベルの出力
    wmesg->setTextFormat(XHTMLText);
    wmesg->setText(L"<div class=\"blk\"></div><br />Выражения: ");
 
    // ユーザ入力内容の表示
    // 変換済ユーザ入力
    WString wdata(udata, UTF8);
    wdata += opts;
    pmesg->setTextFormat(PlainText);
    pmesg->setStyleClass("exp");
    pmesg->setText(wdata);
 
    /* 以下にビジネスロジック cmesg に出力コンコーダンスを格納する。省略 */
 
    // コンコーダンスの出力
    cmesg->setTextFormat(XHTMLUnsafeText);
    cmesg->setText(conc);
}
 
// サービスオブジェクトの生成
WApplication *createApplication(const WEnvironment& env)
{
    return new Concordance(env);
}
 
// main: 各種 Web パラメータとともにサービスオブジェクトを生成する
int main(int argc, char** argv)
{
    /* Web サービスの初期処理を行う。省略 */
    // サービス開始
    return WRun(argc, argv, &createApplication);
}

このように,Concordance クラスとそのクラスメソッドとして,画面,ビジネスロジックを記述して行く。WApplication クラス継承として Concordance クラスを生成し,Wt::WServer::WRun() メソッドでサービスを開始する形態である。

プーシキン・コンコーダンス画面では,テキストボックス,送信ボタン,チェックボックス,ラジオボタンをコンコーダンス・ビジネスロジック動作条件の設定のために用いている。これらの画面要素を Concordance クラスのコンストラクタのなかで Wt Widget オブジェクトとして定義している。 画面の「地」は,WApplication クラスのメソッドである root() によってオブジェクトのポインタが返されるようになっていて,これに紐付けて各種 Widget を new 演算子で生成して行く。

上例では,チェックボックスやラジオボタンを WGroupBoxWButtomGroup といった,ボタンやチェックボックスを纏める Widget のなかに入れており,この場合は root() にバインドされたこれらグループに紐付けてボタン等の Widget を生成する。 root() に付随するすべての画面オブジェクトは,プログラムが終了する時点で再帰的に解放されるので,プログラマがポインタを管理し delete を発行する必要はない。提供 Widget,インクルードすべきヘッダ等,その使い方の詳細は Wt ドキュメントWidget Gallery デモプログラムを参照のこと。

テキストデータは Wt::WString クラスオブジェクトとして扱う必要がある。Wt::WString(char* value, UTF8) のように初期化すれば,std::stringchar* の UTF-8 文字列からも,Wt::WString オブジェクトが得られる。UTF8 という指定は,UTF-8 エンコードデータとして Wt::WString に格納する旨の指示である。直接 UTF-8 文字列を格納したい場合は Wt::WString(L"Выражения") のようにワイド文字定数として書けばよい。ソースコード中の文字列のエンコーディングの扱いはコンパイラ依存ということになっているが,最近の GNU C/C++ Version 4 では UTF-8 として扱うはずである。もしそうでないコンパイラを使っていたり,移植性を重視するなら,\u十六進数 記法によってユニバーサル文字定数を書けばよい。

useStyleSheet("スタイルシートファイル名") でスタイルシートの指定ができる。Widget オブジェクトに対し,setClass()setId() メソッドで class や id の HTML 要素を付加でき,「見た目」のコントールを,指定したスタイルシートで定義することが可能である。

上例の messageResourceBundle().use(appRoot() + "message"); 行は,外部 XML ファイルリソースの指定である。この場合 message.xml という XML ファイル(名称は任意)を指定している。このファイルに,

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 
message.xml: コンコーダンス用メッセージファイル
-->
<messages>
  <message id="header">
    <div id="header">
      <h1>Конкорданс к тексту А. С. Пушкина</h1>
    </div>
  </message>
  <!-- 後略 -->
</messages>

と記述しておくと,Wt::WString::tr("header") によって,message.xml ファイルの header という ID 属性をもつ message タグのテキストが Wt::WString オブジェクトとして与えられ,これを Web 画面に挿入することができる。こうして,画面のテキストコンテンツを別ファイルで管理でき,文言修正のためにプログラム改修しなければならない不都合を回避できる。これらスタイルシートや XML はプログラム動作中でも,修正がすぐ反映される。また,画面ソースなどを通してユーザから見えることはないので,コンテンツのその他の文言や画像リンクなどをユーザから隠蔽することが可能である。

Wt ではアプリケーションを動作させる環境として二種類をサポートしている。ひとつは Wthttpd という独自のスタンドアロン Web Socket サーバとして,いまひとつは Apache2 等の Web サーバから起動する FastCGI のアプリケーションとして動作させることができる。この際,ソースコードはまったく同じであり,これらの違いはリンケージするとき,libwthttplibwtfcgi のどちらを使うかに依存する。プーシキン・コンコーダンスの場合は FastCGI で動作させているので,コンパイル/リンクは以下のとおりである。

% g++ -g -I/usr/local/include -L/usr/local/lib \
  -boost_random -lboost_signals -lboost_system -lboost_thread \
  -boost_filesystem -lboost_program_opions -lboost_date_time \
  -lboost_regex -lboost_locale \
  -licudata -licui18n -licuuc \
  -lwtfcgi -lwt \
  -o concordance concordance.cpp \
  -lturglem -lturglem-russian -lMAFSA

-lwtfcgi -lwt のライブラリ・リンケージ指定が FastCGI 運用の条件である。Boost の各種ライブラリも Wt に必要である(すべてというわけではない)。ICU Unicode ライブラリはコンコーダンスのロシア語正規表現で使うもの。最後の三つのライブラリは Lemmatizer である。

FastCGI を Apache から使えるようにするには,Apache の httpd.conf に,モジュールを追加するだけでよい。モジュールのパスはインストールしたところを指すようにする。

LoadModule fastcgi_module  libexec/apache22/mod_fastcgi.so

Wt Web アプリを FastCGI として動かすには,以下のような設定を外部ファイル(fastcgi.conf)に記述し,Apache が読込む構成ファイルディレクトリに格納する。Alias の指定は必須ではないが,サイトのドキュメントルートからアクセスできるマッピングをしておかなくては,実質利用できない。

<IfModule mod_fastcgi.c>
    Alias /pushkin/lemmatized/ /home/isao/src/pushkin/concordance/
    FastCgiConfig -idle-timeout 600 -maxClassProcesses 20 -maxProcesses 60 \
        -killInterval 1200 -autoUpdate \
        -initial-env CONCORDANCE_CONFIG=beatrice.conf \
        -initial-env WT_AP_ROOT=/etc/wt
    FastCgiServer /home/isao/src/pushkin/concordance/concordance
</IfModule>

上のコンコーダンス・プログラム例でいえば,/home/isao/src/pushkin/concordance/ ディレクトリ(アプリケーションディレクトリ)下にロードモジュール,messeage.xmlstyle.css などのリソースを格納しておく。あと,Wt Widget がデフォルトで参照するスタイルシートや画像ファイルが Wt パッケージの resources ディレクトリにあるので,これをアプリケーションディレクトリ内にシンボリックリンクしておくとよい。

FastCgiConfig 行は必要に応じて指定すればよい。-initial-env パラメータで,アプリに渡す環境変数を設定することができる。通常の Apache 環境変数設定手段である SetEnv ディレクティブは FastCGI では使えないので注意する必要がある。その他のパラメータについては,FastCGI ドキュメント Module mod_fastcgi を参照。

Apache をリスタートすればアプリが動くはずである。Apache エラーログ httpd-error.log に FastCGI 関連の起動メッセージが出力されるので,正しく動きはじめたかがわかる。ここで Permission denied のようなエラーが出て異常終了しているなら,/var/run/wt ディレクトリに Web httpd のユーザ(www, _www など)による書込権限が付与されているか確認するとよい。

FastCGI ではなく Wthttpd で動かすのなら,リンクライブラリを -lwthttp -lwt とすればよい。そして,以下のようにプログラムを起動する。

% concordance (プログラムロードモジュール名) --docroot . \
  --http-address 0.0.0.0 --http-port 8080 &

無事起動できれば,http://host:8080/ URL でブラウザからアクセスできるようになる。

Wt では /etc/wt/wt-config.xml にサーバ動作設定を記述するようになっている。wthttpd の引数指定ともども,詳細は Wt ドキュメントを参照いただきたい。Wt には SVG,PDF を生成する関数なども用意されており,HTML5 にも追従している。これまで C++ Web アプリを CGI のとろいインタフェースにしか結びつけられなかったシステム設計者には,Wt は驚きのライブラリである。

Pushkin concordance, UTF-8 - Ctext 変換

プーシキン・電子コンコーダンス・サービスのコーパスを見直し中である。このシステムでは従来,ロシアで売りに出たプーシキン全集 CD-ROM から抽出した電子テキストを使った。でも詩,小説,物語詩,詩劇などの藝術テクストにコーパスを限定していた。このたび批評,書翰も追加した。これからさらに改善していくつもりでいる。プーシキンの論文を書いたとき,やはり Полное собрание сочинений(完全全集)でないとちょっと不十分だと自分でも認識があった。3.11 のショックで «Медный Всадник»(『青銅の騎士』)を読み直し,ちょっと自分なりの発見をして以来,プーシキンのことばを精密に洗い出すために,このプログラムも改善したいと思うようになった。ロシア文学研究者にも使って欲しいものであるが,残念ながら誰も使わない(その理由は私がいちばんよく知っている。コーパスの完全性とワード出現部位の原典参照機能。そこを改修したい)。

このシステムのコーパスは Ctext - X Window System Compound Text というエンコーディングでないと処理できない。本当は UTF-8 にしたいのだけれど,このプログラムを作った 1998 年当時はエディタなどの環境上の問題で,多言語テキスト用途で Unicode を使うのに踏み切れず,UNIX の世界の多言語テキスト形式である Ctext に準拠した。私は 1993 年から Mule(Emacs 多言語機能の礎になった,日本人の開発した驚くべき多言語エディタ)上で日本語・ロシア語混在の Ctext テキストを作成するようになった。Ctext とは,ISO 2022 国際標準に準拠した,いわゆるエスケープ・シーケンスで既存の言語文字コードを切り換えることによって多言語テキストを実現する符号化方式である。Unicode の CJK 統合漢字の包摂問題がないというメリットがあるが,エスケープ・シーケンスという古色蒼然とした方式であり,部分だけで文字の判断ができないという欠点がある。テキスト・ストリームの先頭部が失われると残りの部分のデコードも困難になる。

時代は変わりいまや UTF-8 でテキストを準備し,Ctext に変換しなければならない。コード変換ソフトウェアは数あるけれども,Ctext をメニューにもつものは,寡聞にして coco(Mule の付属ユーティリティ)くらいしか思い浮かばない。ところが coco は Unicode が扱えない。じゃ,どうするか。Ctext も UTF-8 もサポートする GNU Emacs を,バッチ起動で使うのである。

% emacs -l lisp1 ファイル -l lisp2 -f save-buffer -kill

と,コマンドラインで投入すると,Emacs は lisp1 を読んで評価してから「ファイル」をオープンし,lisp2 に従って処理をしたバッファを「ファイル」にセーブして終了する。この仕様を利用して,lisp1 に UTF-8 としてファイルを開く Emacs-lisp を書いておき,lisp2 にバッファ・コーディング・システムを Ctext に変更するコードをしるしておけば,人間のオペレーションを介さずに,UTF-8 - Ctext エンコーディング変換を自動実行することができる。この lisp1, lisp2 の Emacs-lisp コードはそれぞれ以下のようなものである。

;; utf-8 input script
(set-default-coding-systems 'utf-8-unix)
; chaset priority
(set-charset-priority
 'cyrillic-iso8859-5
 'greek-iso8859-7
 'hebrew-iso8859-8
 'latin-iso8859-1
 'latin-iso8859-2
 'japanese-jisx0208
 'japanese-jisx0212
 'japanese-jisx0213-1
 'japanese-jisx0213-2
 'japanese-jisx0213.2004-1
 'chinese-big5-1
 'chinese-big5-2
 'chinese-gb2312
 'korean-ksc5601
)
; open file encoding
(setq coding-category-raw-text 'utf-8-unix)
;; ctext output script
(set-buffer-file-coding-system 'x-ctext-unix)

入力用に set-charset-priority を指定しているのは,ロシア語 ISO 8859-5 などの優先度を日本語 JIS コードよりも上げておかないと,へんなエスケープ・シーケンスが付いてしまうからである。カレント・ディレクトリにある UTF-8 テキストファイルすべてを Ctext に変換するシェルスクリプトは以下のようになる。-q オプションを付けて Emacs を起動しているので,.emacs の設定如何によらずうまく動くはずである。

#!/bin/sh
for i in *.txt
do
    echo "convert encoding $i from UTF-8 to Ctext."
    emacs -q -batch -l iutf8.el $i -l octext.el -f save-buffer -kill
    mv $i~ $i.back
    echo "backup file saved to $i.back."
done

こんなのは誰も使わない。もはや Ctext はほとんど過去の遺物だから... (とはいえ,Emacs の C-c h で表示される万国こんにちはメッセージ,Emacs で動くメールソフト Mew の Summary では Ctext が健在である)。

それにしても,execute(処刑する) だとか kill (殺す)だとか,コンピュータ用語には不穏なものが多いですね。この場合「実行する」,「停止させる」なんだけど。

エピグラフ・ランダム出力 JavaScript

私は名言集のようなものが好きである。昔の作家は愛する詩人・作家の文言を,象徴的意味を担うエピグラフ(題銘)として,己の作品にしばしば掲げたものである。西洋の著述家には現在もこの伝統が生きている。私もエピグラフの伝統が好きである。でもって,当サイトにも各ページにエピグラフを掲げたいと思い,短い JavaScript プログラムをひとつ書いた。

名言集テキストデータベースから指定した数だけランダムに名言を選択して,出力関数をしるしたブログ箇所にエピグラフを出力する。コード epigraphs.js は以下のようなものである。

// -*- coding: utf-8; mode: javascript; -*-
// Show Epigraphs
function epigraphs(outnum) {// outnum: 出力個数
    // 名言集 配列
    // - 追加するときは以下の形式で。' と , に注意
    //     '<div id="epgr">エピグラフ</div>' +
    //     '<div id="epgrath">— 著者</div>',
    // - 出力様式は id: epgr, epgrath を css で調整する。
    var words = [
        '<div id="epgr">Sator arepo tenet opera rotas<br />' +
        '種蒔く者は収穫を保有し収穫は種蒔く人を保有する</div>' +
        '<div id="epgrath">— ラテン語の格言</div>',
        '<div id="epgr">Hello darkness, my old friend</div>' +
        '<div id="epgrath">— ポール・サイモン</div>',
        '<div lang="grc" id="epgr">πάντα ῥεῖ. 萬物は流轉す</div>' +
        '<div id="epgrath">— Herakleitos</div>',
        '<div id="epgr">夜熟睡しない人間は多かれ少なかれ罪を犯している</div>' +
        '<div id="epgrath">— モーリス・ブランショ</div>',
        // このようにカンマ区切りで,ずらずら,いろいろ,名言を書いておく
        '<div id="epgr">おい小池,そろそろだ</div>' +
        '<div id="epgrath">— 警視庁</div>'
    ];
    // 引数チェック: 数値以外は初期値 2 を設定
    var wlen = words.length; // 名言集個数
    var num = 2;
    if (outnum != null) {
        // 引数指定がある場合
        if (isNaN(outnum)) {
            // 数値でない場合デフォルト
        } else {
            if (outnum > wlen) {
                // 名言集個数以上もデフォルト
            } else {
                num = outnum;
            }
        }
    } // 引数指定がない場合デフォルト
    // 乱数でランダムに指定個数分出力する。
    var alrd = new Array(); // 既出リスト
    var rand = wlen;        // 初期値は起こりえない数値
    var flag = true;        // 出力可フラグ
    while (num--) {
        rand = (Math.floor(Math.random()*1000)) % wlen;
        for (var i = 0; i < alrd.length; i++) {
            if (rand == alrd[i]) {
                // 同じものが選択されたら再度乱数を取得
                num++; flag = false; break;
            }
        }
        if (flag) {       // 初出のもののみ出力
            document.write(words[rand]);
            alrd.push(rand);
        } else {
            flag = true;
        }
    }
}

乱数を取得して三桁の整数値にし,名言データ配列数で割った剰余で出力すべき名言をポイントする。これで実行する都度「ランダム」な選択を実現している。つまりいまの版では,名言集は 1000 個までということになる。あまりに名言の数が多いと読込み時間もバカにならなくなるだろうから,1000 を越えるようなら,名言集を SQLite3 にでも格納して,Servlet がテキストを供給するように書き直した方がよいかも知れない。

名言データベースは上のコードの words[ ] 配列の要素として定義してある。名言とは言っても,私の勝手な思いつきで拾ったものなので,「おい小池,そろそろだ」みたいな愚にもつかないものも含まれている。面白いと思う方は,ご自分で名言集を構築してこのスクリプトを活用していただきたい。私のコードでは,出力スタイルの ID を埋め込むようになっていて,スタイルシートで表示様式をコントロールする前提である。

使い方は,epigraphs.js をロードしておき,エピグラフを表示したいブロックに以下のようにマークアップしておく。ブログならヘッダーテンプレートに書いておくなどすればよい。

<script language="JavaScript">epigraphs(3);</script>

引数(パーレンの中)に出力したいエピグラフの数を指定する。デフォルトは 2。

いま,このブログの右上に出ていますよね? リロードするたびに文句が変るはずである。

MovableType5 移行

このお休み,國芳展に出かける以外はうちでずっと MovableType ブログ管理システム(以下 MT)のバージョンアップ作業をやっていた。これまで Ver. 4 を使っていたんだけど,記事数が 1000 を越えてからカテゴリアーカイブが更新されないなどの不具合が出て,記事を追加するたびに再構築するなどの面倒な対処をさせられていた。でも MT 版更新はデザインの移行がしち面倒くさくてならない。それでも,小さい面倒を毎回強いられるより,MT 5.12 へのバージョンアップという本格的な面倒を一回だけ受け容れることにした。

せっかくなのでデザインを新しくすることにした。Photoshop と Illustrator を用いてバナーやロゴなどの素材を自作した。MT4 のウィジェット設定をいちいち画面でコピーしてテキストファイルに格納して,これらを MT5 のデザイン設定に反映した。スタイルシートの調整がもっとも手間を要した。MT では,あるスタイル要素がいろんなスタイルシートで定義されているので,思うような効果が出ないとき悪さをしている定義を探すのがたいへんである。もっとラクな移行方法があるんだろうけど,MT をきちんと勉強するなんて気持ちはまったく起きない。

過去のブログ記事を MT4 環境でエクスポートし,Emacs や sed で必要な編集を施した。6 年も運用していると,引用などの表示様式に統一がなくなってしまったので,少しは改善しようと思ったのである。ついでに,先日インストールした SyntaxHighlighter をプログラム・コードの部分に全面的に適用した。

この過程で SyntaxHighlighter に Emacs-lisp,LaTeX,コマンドライン用のスクリプトがないのが気に入らなくなり,自作した。言語の追加は正規表現の扱い以外は極めて容易である。Emacs-lisp と LaTeX 用を以下に掲載しておく(コマンドライン用は Bash 用のものを少し訂正しただけなので割愛)。lisp 用は funcs 等の変数に指定したものが色づけされる。LaTeX 用はコメント行,コントロールシーケンスのみが色づけされる設定である。lisp 用,LaTeX 用のマークアップは brush に対してそれぞれ lisplatex を指定すればよい。

/**
 * SyntaxHighlighter Emacs-lisp
 */
;(function()
  { 
      typeof(require) != 'undefined' ?
          SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
 
      function Brush()
      {
          var funcs     = 'lambda list progn mapcar car cdr reverse ' +
                          'member append format';
          var keywords  = 'let while unless cond if eq t nil defvar ' +
                          'dotimes setq listp numberp not equal';
          var macros    = 'loop when dolist dotimes defun';
          var operators = '> < + - = * / %';
 
          this.regexList = [
              { regex: SyntaxHighlighter.regexLib.doubleQuotedString, 
                       css: 'string' },
              { regex: new RegExp('&\\w+;', 'g'), css: 'plain' },
              { regex: new RegExp(';.*', 'g'), css: 'comments' },
              { regex: new RegExp('\'(\\w|-)+', 'g'), css: 'variable' },
              { regex: new RegExp(this.getKeywords(keywords), 'gm'), 
                       css: 'keyword' },
              { regex: new RegExp(this.getKeywords(macros), 'gm'),
                       css: 'keyword' },
              { regex: new RegExp(this.getKeywords(funcs), 'gm'), 
                       css: 'functions' }
          ];
      };
   
      Brush.prototype = new SyntaxHighlighter.Highlighter();
      Brush.aliases   = ['lisp'];
      SyntaxHighlighter.brushes.Emacs = Brush;
      typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
  })();
/**
 * SyntaxHighlighter LaTeX
 */
;(function()
  { 
      typeof(require) != 'undefined' ? 
          SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
 
      function Brush()
      {
          this.regexList = [
              { regex: new RegExp('%.*','gm'),  css: 'comments' },
              { regex: new RegExp('\\\\\\w*','gm'), css: 'keyword' }
          ];
      };
   
      Brush.prototype = new SyntaxHighlighter.Highlighter();
      Brush.aliases   = ['latex', 'tex'];
      SyntaxHighlighter.brushes.Latex = Brush;
      typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
  })();

今回のブログ・デザイン変更でいちばん困ったのは,背景色変更に伴う画像の扱いである。背景色はこれまで白(RGB で示すと #ffffff)だったのを明灰色(#e8e5e1)に変えたのだが,すでにゴマンとある画像が白の背景色を前提としているためにドロップシャドウなどが白抜きになっていて,これを明灰色の背景に配置すると白の枠が大いに目立って醜いことこの上ない。例えばこんな感じになってしまう。

sample

この対策のために何百とある画像を作り直すのは現実的でない。背景色になじむ枠画像と重ね合わせる調整をスタイルシートで行うことなどを考えたが,ブラウザによっては配置がずれたりしてさらに醜くなるかも知れず,うまくない。結局,ImageMagick によって色の置換を行うことにした。

ImageMagick には convert ユーティリティが付属していて,各種画像変換が可能である。

convert -fill '#rgb-after' -opaque '#rgb-pre' prefile afterfile

とやると ファイル prefile の RGB 値 rgb-preの色を rgb-after に置き換えて afterfile に出力してくれるのである。しかも -fuzz nn% オプションを付加すれば,指定した度合いでファジーに中間色を置き換えてくれる。いくつかパーセンテージを変えて試したところ,-fuzz 10% くらいがよい感じであった。そこで,tcsh コマンドライン上で以下のようにして画像を一括変換した。もちろん,エクスポートしたブログ記事テキストの画像ファイル名拡張子を .png に置換しておかなければならない。

% foreach i (*.jpg)
foreach? set bn=`basename $i .jpg`
foreach? convert -fuzz 10% -fill '#e8e5e1' -opaque '#ffffff' $bn.jpg $bn.png
foreach? end
%

これで上の画像は以下のとおりとなり,背景色に対してごく自然になじむようになった。UNIX のコマンドライン・ユーティリティの文化は偉大である。ただし,白にごく近い色への変更でないとこの方法は通用しない。また,当然ながら画像の #ffffff の白がすべて #e8e5e1 に置き換わるので純白の画面がおとなしくなってしまうだけでなく,文字を含む画像では少し字が瘠せてしまうので,手放しでよいというわけではない。それでも私の目的には充分であった。Web に掲載する画像にドロップシャドウを付ける場合は,白抜きの部分をアルファチャンネル(透過)にして PNG で作成しておくべき — 今回の教訓。

sample

エクスポート・ブログ記事テキストの編集が終わったら,新 MT5 環境でインポートし,再構築して,過去の記事の取り込みは完了した。MT5 にバージョンアップして,1000 を超過した記事のカテゴリ処理もうまく行くようになっただけでなく,再構築がかなり高速になった。

新年のスパムメール

自宅サーバでメールサーバを運用し,DynDNS で取得したドメイン名でメールをやり取りしている。当然ながらスパムメール・チェッカ SpamAssassin やメール振分ソフト Procmail を稼働させて,メールボックス配信時にスパムメールを取り除けている。一般に,スパムと判断されたメールは,これを /dev/null 行きにする管理者が多いのではないだろうか。/dev/null は UNIX のスペシャルファイルのひとつで,これを出力先にすると削除と同じ操作となる,言わば宇宙空間のブラックホールのようなものである。

しかし,私はスパムをブラックホール送りにしないで専用ディレクトリに入れている。もちろん,きちんとしたメールがスパムと誤判断される場合を考慮したからであるが,たまにスパムの文面をみて大笑いするためでもある。この際,FreeBSD サーバ上の GNU Emacs 環境の MUA Mew を使って閲覧する。Windows では Outlook Express などでプレビュー表示しただけで感染してしまうウィルスが知られているが,UNIX 上のテキストベースの Mew なら,いかがわしいスパムを閲覧してもウィルス感染する心配はあんまりない(だから Windows ユーザは私の真似をしてはいけない)。

私はサイトのあちこちに自分のメールアドレスを晒しているので,ロボットに収集されて,いろんなところからスパムが私のところに来る。これらスパムは,かつては英語のものが圧倒的に多かったが(ほとんどが「あなたの dick を三倍に」,「Rolex 高級時計を格安で」に類するものであった),この二週間ほどの傾向では,日本語が約 50%,中国語が約 30% である。英語のものは 10% 程度である(米国は何か対スパムの法整備をしたのか?)。ウクライナ/ロシアからも 3% 程度ある(ウクライナはアタックも多く,ならず者計算機使いが多いと私はみている)。中国語が目立つようになったけれども,やっぱり懲りない奴らは日本語を話すのが多いらしい。エロサイト,「金持ちマダムのお相手しませんか」的出会いサイト,裏ビデオ取引への勧誘がほとんどである。サーバアタックは米国,ウクライナ,中国からが多かった(ちょっと前の調査による)のだけれども。もちろん,これは「私のところに来たもの」(しかも二週間のデータ)がベースなので,スパムメール事情の全体的傾向とは判断できない。

年明け,こんなスパムが来た。「パックリおまんこ開けましておめこでしょう」。「あけおめコ」というのもあった。「あけましておめでとう,ことしもよろしく」というのを若い人たちは「あけおめことよろ」と略したりする。このスパムメール,そのなかに卑猥な言葉が隠れていることを利用していて,大笑いさせられた。このスマパー,きっと関西人に違いない。メールヘッダを確認すると,どの IP から送付されて来たかがわかる。この「あけましておめこでしょう」メールヘッダは,次のようなものだった。

From info@027ab.cdefg.info  Thu Jan  5 14:19:11 2012
Received: from 027ab.cdefg.info ([175.106.71.27])
	by beatrice.yasuda.org (8.14.4/8.14.4) with SMTP id s055R94k001229
	for isao@yasuda.homeip.net; Thu, 5 Jan 2012 14:19:10 +0900 (JST)
	(envelope-from info@027ab.cdefg.info)
Message-Id: <201201050519.s055R94k001229@beatrice.yasuda.org>
MIME-Version: 1.0
Content-Type:text/plain; charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit

IP は 175.106.71.27(スパマーの IP アドレスは晒してやってもかまわない?)。人間ではなく機械が自動的にバラまいたスパムであることがわかる(当然といえば当然)。人間がパソコンから出すメールはたいていプロバイダのメールゲートを通過し,そのたびに Received: 行が追加されるので,ヘッダにはこれが必ず複数行ある。上記ヘッダには 1 行しかないので,MUA が直接私のメールサーバに接続したことがわかり,つまりスパム発送プログラムのしわざだと考えられるのである。じゃ,いったいどこが発信源か。whois コマンドで調べてみる。

% whois 175.106.71.27
...
inetnum:        175.106.64.0 - 175.106.127.255
...
person:         jung bxung wxx
address:        Gyexnggi-dx Gwxnsexn-dxng
address:        1056-XX XX  JNDINFO.CX
country:        KR
phone:          +82-31-226-XXXX
e-mail:         comnetxx@hanmir.xxx
mnt-by:         MNT-KRNIC-AP
changed:        hostmaster@nic.xr.kr
source:         KRNIC
...

内容は xxx などと少し改変し,情報量もはしょってある。発信源は韓国だったニダ。関西じゃありませんでした。このメール本文には,スパムとしては当然ながら,誘導したいエロサイト URL がしるされているが,この URL から引いた IP アドレスは米国のサイトのようである。この日本語スパマーは韓国,米国を隠れ蓑にして商売していやがったわけだ。

と,ま,時折りこのようにスパムを眺めて楽しんでいるのであります。下品なネタですみませんでした。

misima 漢詩作成支援: 平仄音韻分析・詩語検索 misimakansiservlet を使っている方からメールをいただいた。そこで思い出したように,久しぶりに辞書のメンテナンスを行った。平仄などのデータを格納した辞書=漢字データベースは SQLite3 で作成したのだけど,SQLite3 オペレーションを中心とするメンテナンス手順を忘れてしまっていた。思い出したのを整理し,ここにも備忘録を残しておく。以下,beatirce:/home/isao[nnnn] % は misima サーバ機端末の tcsh プロンプト,nnnn はコマンド履歴番号を示す。

misimakansiservlet において,漢詩分析デーモン misimakansiserver は漢字データベース KANJITBL で見つからなかった文字をログに出力するようになっている。私はこれを定期的にチェックし,それらの辞書にない文字(ユーザが入力したが辞書になかった漢字)を手作業で DB に追加する。ログのその部分を抽出する。

% grep -e '\*\*\*' /var/log/kansiserver.log | sort | uniq
***** 㱕 が KANJITBL にありません.
***** 僲 が KANJITBL にありません.
***** 嵆 が KANJITBL にありません.
***** 扃 が KANJITBL にありません.
***** 涴 が KANJITBL にありません.
***** 疴 が KANJITBL にありません.
***** 裛 が KANJITBL にありません.
***** 髥 が KANJITBL にありません.

というわけで,今回この 8 文字を追加。漢字字典を調べ,平仄,読み,語義などを DB のソースコード kanji.src にテキストエディタで追加する。完了したら,create table で漢字データベースファイル KANJI.db を生成する。そしてこれに kanji.src の内容を .import コマンドでロードする。kanji.src のデータ記述はカンマ区切りの CSV 形式なので,ロード実行には -separator , オプションを指定している。

% cd misima
% sqlite3 KANJI.db < mkkanji.sql
% sqlite3 -separator , KANJI.db ".import kanji.src KANJITBL"

ここで,mkkanji.sql とは create 文で DB のスキーマ(データ項目とその属性)を定義する元ネタである。misimakansiservlet の場合は以下のようなものである。

-- -*- coding: utf-8; mode: sql; -*-
-- misimakansiservert 平仄音韻分析用漢字テーブルレイアウト
-- 漢字 ji, 平仄 hs, 韻 in1, 韻 in2, 音読 yo, 訓読 yk, 備考 bk
create table KANJITBL (
       ji text not null,
       hs text not null,
       in1 text not null,
       in2 text not null,
       yo text not null,
       yk text not null,
       bk text not null
);

これで漢字データベースはできた。データ内容を確認して問題なければ,/usr/local/etc/misima/ の下にコピーする。これで Web サーブレットから使えるようになる。以下,SQLite3 DB 操作のメモ。

  1. SQLite3 コマンドラインの起動
  2. 扱う DB を引数に指定する。
    % sqlite3 KANJI.db
    SQLite version 3.7.7.1 2011-06-28 17:39:05
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite>
  3. テーブル名の表示
  4. sqlite> .table
    KANJITBL
  5. DB スキーマの表示
  6. sqlite> .schema KANJITBL
    CREATE TABLE KANJITBL (
               ji text not null,
               hs text not null,
               in1 text not null,
               in2 text not null,
               yo text not null,
               yk text not null,
               bk text not null
    );
  7. DB 件数の確認
  8. sqlite> select count(*) from KANJITBL;
    6929
  9. tcsh コマンドラインから直接 SQL を入力する
  10. sqlite3 [opt] [db-file] に続けてクオート(ダブルクオーテーションマークで括る)して SQL 文を書けばよい。SQLite3 は標準入力からも SQL を受け付けるので,テキストファイルに書いておきリダイレクトで読み込ませてもよい。
    % sqlite3 KANJI.db "select * from KANJITBL where ji='髥';"
    髥|◎|hs:14:塩|sk:29:艷|ぜん;ねん|-|ほおひげ;鬚髯:柔らかいあごひげとほおひげ;
    % cat sql.txt
    select * from KANJITBL where ji='㱕';
    select * from KANJITBL where ji='僲';
    select * from KANJITBL where ji='嵆';
    select * from KANJITBL where ji='扃';
    select * from KANJITBL where ji='涴';
    select * from KANJITBL where ji='疴';
    select * from KANJITBL where ji='裛';
    select * from KANJITBL where ji='髥';
    % sqlite3 KANJI.db < sql.txt
    㱕|○|hk:05:微|-|き|かえる;かえす|帰・歸の異体字;
    僲|○|hs:01:先|-|せん|-|仙人;僊の異体字;
    嵆|○|hk:08:齊|-|けい;げ|-|稽古の当て・古いことを集めて考える;嵆山:山の名;嵆康:姓の一;
    扃|○|hs:09:青|-|けい;きょう|-|かんぬき;とざす;
    涴|●|sj:13:阮|sk:21:箇|えん;おん;わ|-|涴演:川が曲がりまわって流れるさま;けがす・けがれる;
    疴|◎|hs:05:歌|sk:22:禡|あ;か;け|-|やまい;痾に同じ・痾疾;
    裛|●|sn:14:緝|sn:16:葉|ゆう;おう;よう|-|つつむ・香がまとわりつく;書物の覆い;しっとりと濡れる;
    衣の部;
    髥|◎|hs:14:塩|sk:29:艷|ぜん;ねん|-|ほおひげ;鬚髯:柔らかいあごひげとほおひげ;

これで追加した漢字の平仄分析ができるようになった。
 

20111008-kanjituika.png

馬込文士村散歩

Movable Type 5.1 RC が出たので,ちょっと試してみた。でもカスタマイズをもう一度反映するのにひどく手間がかかりそうだったので,Ver. 4 に戻すことに。ところが,とってあったバックアップからの復元がエラーになり,ハマリまくった。Movable Type のバックアップ・ツールは信用しないほうがよい。Web 環境全体をごっそりコピーするほうがシンプルにして確実である。

Facebook でロシアの現代女流詩人オリガ・セダコヴァからメッセージをもらった。彼女の詩集や紀行文,プーシキン関係論文,教会スラヴ語関連学術書を読んで,私は彼女の作風に強く惹かれていたので,信じられないくらいうれしかった。インターネット時代となり,友達の友達を六段階くらい辿るとたいていの人が繋がり合うという話があるけれども,ひと昔前ならこんなことは考えられなかった。

また,同じく Facebook で,ロシアの作曲家ガリーナ・グリゴーリエヴァから,すみだトリフォニーホールで開催される東京カンタート・クロージング・コンサートへの招待があった。彼女はこの催しに招かれて来日するという。そこで彼女の合唱曲『生の夜』が演奏されるんである。「ぜひお会いしましょう」とのこと。こちらもうれしくてならない。だけど,当日はすでに別の用事が入っており,どうしようか悩んでいるところである。
 

* * *

連休に入り,妻と馬込文士村周辺を散歩した。大田区馬込・大森山王界隈は,かつて,萩原朔太郎,室生犀星,北原白秋,日夏耿之介,吉屋信子,三好達治,北園克衛,和辻哲郎などなど,錚々たる作家・詩人たちが住まったことで,馬込文士村と呼ばれている。若き日の川端康成,三島由紀夫も住んでいたらしい。大田区郷土博物館で,文士ゆかりの品々を見学した。北原白秋や日夏耿之介,萩原朔太郎の自筆原稿,初版本など,興味深かった。
 

20110501-bunshimura.png

左は大森駅山王口すぐにある馬込文士村の作家・詩人たちのレリーフ。ちょっとゾンビみたいで怖い。夜にはこの前を歩きたくないものである。右は博物館にあった日夏耿之介の雑誌『戯苑』のカバー。
 

20110501-hinatsu-tanka.png

この詩歌集にあるような活字の質感は,いまや失われてしまった。日夏耿之介の『黒衣聖母』の黝い浪漫主義には,あの凹凸感のある旧字体活字こそが相応しい。彼の詩は,紋切型のおどろな物語性がなんとも鼻に付くのだが,それゆえにこそ私にとっては堪らない魅力がある。

怕る怖る眺めあげる
黄金色の礼拝壇の中央には
端厳美麗の永貞女彳みたまふ
その眼前の赤蠟燭の大柱の
とぼとぼと燃えさぐる灯の睛のいろは
眼疾を疾んだのか
気弱い灯影疼みただれて
白い泪はしめじめと
紅艷の燭柱の素肌を泫れる
悪 いかほど淫慘な柔肌か
日夏耿之介『蠱惑の人形』—『日本の詩歌』12,中公文庫,1976 年,115 頁。

佐藤惣之助のコーナーに,『六甲颪』,そう,あの阪神タイガースの応援歌の詞が掲げられていた。佐藤の作詞によるもの。プロ野球がやっと開幕,楽しみがまた増えたわけであるが,残念ながら,どうやら今年もわが真弓阪神タイガースはダメ虎のようである。おまけに金本アニキの連続出場記録も,どっちらけの終焉。アニキを温存せぇとは言わんけどやで,配慮が足らなさ過ぎやおまへんか。「意を決して」,ではなく,「判断ミス」で記録が途絶えた,なんていったいなんなんだ。アニキがカワイソ。なんの覚悟もない,ブレーン任せの判断誤りばかりで,まったく存在感,リーダシップが感じられないこと — 真弓監督,菅総理の共通点である。

その他,博物館展示品では,坪田譲治宛萩原朔太郎書翰の,罫を無視した大胆な筆跡が印象的であった。それにしても,昔の詩集・小説本は装丁が奇抜で,工芸的意匠に溢れているんである。

文士村散歩といっても,周辺は細い道が入りくねった閑静な高級住宅街であって,地図を片手に歩いても,番地をたよりに道を探すのに苦労し,お金持ちの家が眼につくばかりで,なんの面白みもなかった。北園克衛が住んでいたという場所を探したのだけれども,まるでわからなかった。12,000 歩ばかり歩いて適度な運動をして来たという感じ。

大森駅に着いたらハラがペコペコ。喜多方ラーメン坂内食堂を見つけ,ラーメンと餃子を食った。アサヒ・スーパードライが腹に沁みて旨かった。
 

The One Ring, Tengwar fonts

J. R. R. トールキンの『指輪物語』にテングワールという謎めいた文字が出て来る。架空の言語は謎めいたファンタスティックな世界の完結性をこの上もなく表わしている。あの恐ろしい力を持つ The One Ring にモルドール語で刻印された火文字を,LaTeX tengwarscript パッケージで組んでみた。

20110418-mordor.png

One ring to rule them all, one ring to find them,
One ring to bring them all and in the darkness bind them.
一つの指輪は,すべてを統べ,一つの指輪は,すべてを見つけ,
一つの指輪は,すべてを捕えて,くらやみのなかにつなぎとめる。
J. R. R. トールキン『指輪物語1』瀬田貞二・田中明子 訳,評論社,1992年,p. 112.

tengwarscript は CTAN: macros/latex/contrib/tengwarscript/ にある。バージョンは 1.3。フォントは何種類も出回っているが,私は AnnatarFormal だけを組込んで試してみた。上の画像は Annatar Italic である。流麗でなんとも美しい。原稿は以下のとおり。このモルドール語テクストはパッケージ添付 tengtest.tex にサンプルとして掲載されている。これを pdflatex で処理した。

% -*- coding: utf-8; -*-
% Tengwar on The One Ring from "The Load of the Ring" 
\documentclass[12pt]{article}
\usepackage[pdftex]{color}
\usepackage[T1]{fontenc}
\usepackage[all]{tengwarscript}
\definecolor{charc}{cmyk}{0,0.77,0.87,0}%
\begin{document}
\pagestyle{empty}
\color{charc}
\begin{center}
\tengwarannataritalic[2.0]
\tengwa{254}
\Textendedcalma\TTthreedots\Tnuumen\Tessenuquerna\TTthreedots%
\Tungwe\Tando\Toore\TTrightcurl\Tumbar\Ttinco\TTthreedots%
\Tlambealt\TTrightcurl\Tquesse\TTdoublerightcurl
\Tromanperiod\Ts
\Textendedcalma\TTthreedots\Tnuumen\Tessenuquerna\TTthreedots%
\Tungwe\Tungwe\Tumbar\TTnasalizer\TTdot\Ttinco\TTthreedots%
\Tlambe\TTrightcurl
\tengwa{255}\\
\Textendedcalma\TTthreedots\Tnuumen\Tessenuquerna\TTthreedots%
\Tungwe\Tthuule\Troomen\Tquesse\TTthreedots\Ttinco\TTthreedots%
\Tlambealt\TTrightcurl\Tquesse\TTdoublerightcurl
\Tromanperiod\Ts
\Textendedungwe\TTthreedots\Tumbar\Toore\TTrightcurl%
\Tesse\Tkern{-0.2}\Tmalta\TTrightcurl%
\Textendedcalma\TTdot\Ttelco\TTdot\Tquesse\Troomen\Tparma%
\TTnasalizer\TTdot\Ttinco\TTthreedots\Tlambe\TTrightcurl
\end{center}
\end{document}

tengwarscript のインストールは通常の LaTeX パッケージ組込となにも変わらない。ダウンロードした tengwarscript リソースを TDS に準拠して TeX ツリーに格納し,tengwarscript.mapupdmap-sys で登録する。フォントもほぼ同じだけれど,Formal については TrueType フォントファイル名を tengwarscript.map の記載に合わせてリネームする必要がある(もちろん map を訂正してもよい)。テングワール文字の出力命令については,ドキュメント tengwarscript.pdf を参照。
 

※ 4.20 付記

一応,端末上のインストール・オペレーションもメモっておく。端末操作は tcsh シェルである。TeX ツリーはローカル texmf とし,そのトップ・ディレクトリ名称を /usr/local/texlive/texmf-local としている。

まず,作業ディレクトリ(ここでは ~/tmp/tengwar)で tengwarscript パッケージをダウンロード,展開し,dtx, ins ファイルを latex 処理する。dtx はドキュメントでもあり,以下の操作ではリファレンス解決のため 2 回実行している。もとより PDF はすでに添付されているのできちんとした dvi ファイルが不要ならば,1 回の処理でもよい。

% mkdir -p ~/tmp/tengwar
% cd ~/tmp/tengwar
% set WGET="wget -nH -nd"
% set TEXDIR="/usr/local/texlive/texmf-local"
% $WGET http://mirror.ctan.org/macros/latex/contrib/tengwarscript.zip
% unzip tengwarscript.zip
% cd tengwartscript
% latex tengwarscript.dtx
% latex tengwarscript.dtx
% latex tengwarscript.ins

次に,Annatar 及び Formal フォントをダウンロード,解凍する。Formal フォントについては,map の記述に合うようにファイル名を変更する。

% $WGET http://home.student.uu.se/j/jowi4905/fonts/tngan120.zip
% unzip tngan120.zip
% $WGET http://tengwarformal.limes.com.pl/fonts/TengwarFormal-12c-ttf-pc.zip
% unzip TengwarFormal-12c-ttf-pc.zip
% cd TengwarFormal-12c-ttf-pc/fonts/
% mv TengwarFormal12b.ttf TengwarFormal12.ttf
% mv TengwarFormalA12b.ttf TengwarFormalA12.ttf

su -m で特権ユーザとなり,上で展開したリソースを TeX ツリーに格納し,フォントマップ登録を行う。

% cd ~/tmp/tengwar/tengwarscript
% su -m
# mkdir -p $TEXDIR/{doc/latex,tex/latex,fonts/\
{tfm,vf,type1,truetype,map/dvips,enc/dvips}}/tengwarscript
# cp -p *.pdf $TEXDIR/doc/latex/tengwarscript/
# cp -p *.cfg *.sty $TEXDIR/tex/latex/tengwarscript/
# cp -p tfm/* #$TEXDIR/fonts/tfm/tengwarscript/
# cp -p vf/* $TEXDIR/fonts/vf/tengwarscript/
# cp -p map/* $TEXDIR/fonts/map/dvips/tengwarscript/
# cp -p enc/* $TEXDIR/fonts/enc/dvips/tengwarscript/
# cp -p *.ttf $TEXDIR/fonts/truetype/tengwarscript/
# cp -p TengwarFormal-12c-ttf-pc/fonts/*.ttf \
$TEXDIR/fonts/truetype/tengwarscript/
# mktexlsr
# updmap-sys --enable Map=tengwarscript.map

以上。

後を囘顧みたれば

「ロト,ゾアルに至れる時日地の上に昇れり ヱホバ硫黃と火をヱホバの所より卽ち天よりソドムとゴモラに雨しめ 其邑と低地と其邑の居民および地に生ふるところの物を盡く滅ぼし給へり ロトの妻は後を囘顧みたれば鹽の柱となりぬ」。

『創世記』19 章 23−26,罪と頽廃の街・ソドムとゴモラが神の劫火に滅び,ただロトの家族のみが神の救済に与るくだりである。引用は日本聖書協会による文語訳からのものである(『舊新約聖書』日本聖書協会, 1991 年, p. 21)。クライシスとそこからの救済というモチーフ。震災ゆえかなにか心に迫るところがあり,読み返してみた(もちろん震災被災地はソドムでもゴモラでもない)。ロトの妻は,後ろを振返るなと天使から諭されていたにもかかわらず振返ってしまい「鹽の柱」と化す。これが恐ろしい象徴として胸を打つ。「後」とは過去のことを言うのだろうか。滅びて行くものに後ろ髪を引かれ,それゆえに己をも滅ぼす。私も振返って己を滅ぼすクチだと思う。それにしても,この後ろを振返ることで本意が遂げられないというモチーフは,オルフェウスとユーリディケーの逸話にもあって興味深い。

泉鏡花の怪異短編小説『黒壁』(明治二十七年)に「予も何となく後顧(うしろぐら)き心地して」という表現が出て来る。「後ろ暗い」が「後ろを顧みるような」と等価の関係で捉えられている。「後ろ暗い」とは「気掛かり,心配だ」のほかに「ふた心がある」という意味もある。つまり,心に覚悟がなく,信念に反する邪念があるの謂いである。神はロトの妻のそういう心の奥底を「振返るな」との命により試したのかも知れない。

『創世記』のこのくだりを教会スラヴ語訳聖書でも読んでみた。「鹽の柱」は «столпъ сланъ»「霜の柱」という訳になっている。拙作 OldSlav 教会スラヴ語 LaTeX パッケージ (See also OldSlav: An extension of SlavTeX for Old Church Slavonic typesetting) を使って,タイプセットしてみた。

bytie19-23-26.png

LaTeX の原稿は以下のとおり。

% -*- coding: utf-8; -*-
% Ветхая Библия 19:23-26
\documentclass[b5paper]{article}
\usepackage[pdftex]{color}
\usepackage[T2A,T1]{fontenc}
\usepackage[russian,oldchurchslavonic]{babel}
\languageattribute{oldchurchslavonic}{utf8,slavdate}
\setlength{\textwidth}{11cm}
\DeclareFontFamily{LST}{cmr}{}%
\DeclareFontShape{LST}{cmr}{m}{n}{<-> s * [1.12] fslavrm}{}%
\definecolor{bibc}{rgb}{0.40,0.44,0,20}%
\begin{document}
\color{bibc}
\Large
С'олнце вз'ыде над\ъ з'емлю,
л'ѡтъ же вн'иде въ си\-г'ѡръ.
\И г|сдь <ѡдожд`и на сод'омъ \и
гом'орръ ж'упелъ, \и "ѻгнь ѿ г|сда
съ небес`е.
\И преврат`и гр'ады сї^ѧ, \и вс`ю
<ѡкр'естную стран`у, \и вс'ѧ жив'ущыѧ
во град'ѣхъ, \и вс^ѧ прозѧб^ающаѧ
ѿ земл`и.
\И <ѡзр'ѣсѧ жен`а єг`ѡ всп'ѧть,
\и б'ысть ст'олпъ сл'анъ.
 
\vspace{.4em}
\hfill БЫТЇ`Е ГЛАВ`А \slnum(19).:
\slnum(23).{\selectlanguage{russian}--}\slnum(26).
\end{document}
 
* * *

震災の影響で国産煙草が品薄になっている。一週間ほど前,会社からの帰途にある煙草屋に寄るも,どこもハイライトが品切れだった。帰宅して,近所を探しまわったがやはり品切れ。しようがなく昔住んでいた平間の煙草屋に行ってみるとかろうじて自販機に残があり,店のオヤジにタスポを借りて4箱手に入れた。それ以後,もう入手できない。ま,煙草なんて生活必需品ではないのだから,どうでもいいことではある。
 

* * *

まったく関係ありませんが,麻雀ゲームで清老頭を上がってしまいました。
 
20110417-tinroto.png

家族でラーメン・片付け

娘がうるさくせがむので,家族で外食。JR 鹿島田駅近くの『原製麺所』なるラーメン屋に行った。黒胡麻担々麺,カレーラーメンの旨い私のお勧めの店。皆満足。

統一地方選挙が終わったところ。大学生にもなって AKB48 に現を抜かす息子が — 別にいいじゃねぇか — ラーメンを食いながら,AKB 総選挙のことを話題にした。「なんじゃ総選挙って?」— うじゃうじゃいる AKB ガールの,ファンによる人気投票らしい。テレビで歌って躍っているこの女の子たちに対して,私にはただただ自分の子供を見るような保護者的視線しか持ち合わせていない。CM の「当り前田アツコ」でやっと前田敦子さんの名前を覚えたくらいなんである。へぇー,芸能界にも民主主義のトレンドか。あるいは,芸能界の厳しい臆面のない序列化潮流なのか。AKB48 のファンはこの選挙権を得るために彼女たちの CD を買うんだそうである。血道を上げるファンのなかには 1 CD 当り得られる 1 投票権のために何枚も同じ CD を買うらしい。最近 CD が売れないという話を聞くけれども,おかげで AKB の CD は近年稀にみるミリオンセラーを記録した。この女の子たちは政令指定都市の首長と同じくらいの規模の投票でしのぎを削るわけである。凄い。一番人気の者と同時にドンジリも明らかになり,民主主義ゆえの残酷がここにはある。顧客(ファン)の意思を最重要視するという発想が顧客の参画意識を刺激し顧客の思い入れを増大させ,これにより彼女たちの人気をさらに上げることが出来る。商売の巧みに感心。これビジネス特許ものではなかろうか。それにつけても,AKB 総選挙に比べりゃ,統一地方選挙有権者の真剣度合いの貧弱さが想像され,笑ってしまう。

家の片付けをした。壊れたプリンタ,パソコン,オーディオを廃品回収業者に,ダンボール四箱の本をブックオフに,引き取らせた。床にぶちまけていた岩波日本古典文学体系本 102 巻をやっと書架に収めた。喪失してしまっていた私の ZIPPO 50 年モデルを妻が見つけた。学生時代に購入した懐かしい一品。老斑のような拭えない傷・錆。その後新たに購入した 50 年モデルと比べると,古いの(写真左)はエッジの丸みが強く,やっぱりこちらのほうが古色がある。オイルを注入しフリンツを装填したら,勢いよく点火した。

20110416-zippo.png

新常用漢字表

「𠮟」という文字がいまあなたのモニタに表示されているだろうか? 私の愛用する Mac OS X Snow Leopard: Safari 5.0.4 あるいは Mac OS X Tiger: Safari 4.1.3 ならしっかり見えるが,Windows ユーザはどうだろうか? Windows 7 なら大丈夫か。XP ユーザは見えないのではないだろうか? なんでこんなことを記すのかというと,昨年,11 月 30 日に告示された改訂常用漢字表にこの文字が追加されたからである。1981 年以来の改訂である。なんとその前は 1946 年。

上の「𠮟」という文字は「しかる」という訓を持つ字である。口偏に「七」と書く「しかる」。「しかる」と入力して仮名漢字変換されて普通に出て来る「叱」なら,おそらく誰のモニタにも表示されるはずである。ところが,いつもパソコンで入力している「叱」という文字のほうは誤字であって,「𠮟」こそがじつは正しい「しかる」なのだ(『字源』には「俗に叱と作るは非」とある)。JIS がはじめのボタンを着け間違えた次第なのである。ここに来て常用漢字表に正しい字体が入れられた。

文字「𠮟」は Unicode CJK Unified Ideograph U+20B9F というコードポイントを持つ。いわゆるサロゲートペアと呼ばれる拡張領域(Extension B)に配置されている(JIS では JIS X 0213:2000 第三水準区点 4752)。この領域をきちんと読書きできるソフトウェアはまだ多くないのではなかろうか。私はここで「𠮟」の文字を Mac OS の「ことえり」で入力したわけではない。多言語テキストエディタ GNU Emacs 24 の ucs-insert 関数に 20b9f を指示して得られたものを,コピペしたんである。

そう,そんな特殊なところにある文字(正しい文字なんだけど)が常用漢字表に収録されたんである。このことはいまあんまり騒がれていないが,官庁,地方自治体はもとより,出版や報道業界にも少なからざる影響が出るはずである。その筋のシステム屋は,もうすでに,これら常用漢字表にあるにも拘らずシステムで表示・印字できない文字の取扱いに悩まされているはずである。大騒ぎにならないうちになんとかしなくっちゃと。

官庁・地方自治体のシステムは,つい最近に設計された新しいものは別として,Unicode 対応すら進んでいないのが実情だと思う。文字コードは JIS X 0208:1983, :1990 準拠が基本ではなかろうか。漢字コード草創期の設計を踏襲して大きくなって来た官庁・自治体システムは,人名許容漢字の法令に従わなければならず,貧弱な JIS X 0208 を補うために,JIS 第一水準/第二水準にない文字のために大量の外字を抱えている。そして,文字周りはいろんなところに影響するので,おいそれとは改修できない。(同じようなことを JIS 2004 についても書いたような気がする。)

JIS の独善的標準なら「システム仕様として対応してません」で済むかも知れないが,こと日本国政府ご指定の「常用漢字」に扱えない文字があるとなると深刻である。国民との文書交換を考慮すれば,世の官庁・自治体もこの常用漢字改訂で Unicode 対応システム改修に重い腰を上げることになると思う。また税金が本質的でないことのために消えて行くというわけである。漢字・仮名遣いという悪魔的正書法に立つ日本は,いつまでたっても文字を巡る混乱から解き放たれない宿命にある。「歴史的仮名遣いに戻せ」云々のエセ文化人による戯言よりも,もっと高度で抜差しならない宿命である。
 

* * *

ことのついでに,「𠮟」のようなサロゲートペア文字をどうしても入力できないとき,Perl で出力させるワンライナーを示しておく。Perl 5.8.5 くらいのバージョンが必要だと思う。

端末コマンドラインから次を叩くと,「𠮟」(=U+20B9F)が出力される。(※ 4.27: 少し簡略化した)

% perl -e 'binmode STDOUT, ":utf8"; print pack("U", 0x20b9f);'

あるいは,

% perl -e 'binmode STDOUT, ":utf8"; print "\x{20b9f}";'

20b9f のところに必要な文字の Unicode コードポイント十六進数を指定する。20bb7 を指定すると「𠮷」(吉野家のつちよし)が出力される。この出力をファイルにでもリダイレクトしてコピペして利用すればよい。Mac OS X Tiger 以降のターミナルなら端末上にきちんと文字が表示されるので,それをコピーすればよい。もちろんターミナルの LC_ALL 環境変数は ja_JP.UTF-8 に設定されていなければならない(これはデフォルトのはず)。

Unicode コードポイント十六進数は,学研の『漢字源』なら JIS コードとともに掲載されている。何の役にもたたない『JIS 漢字字典』なんて高価本を買うのはやめて,ぜひこちらを座右に置いていただきたい。私は改訂第四版を愛用しているが,最近第五版が刊行された。

Windows Vista, Windows 7 なら IME パッドから CJK Unified Ideograph Extension B 領域の文字を入力できると思う。Office も 2007 ならきちんと使えるはずである。
 

 

※ 4.26 付記

この記事を書いたあと,ネットを渉猟していたら,京大の安岡孝一先生(文字コードに関する権威でもある。日本語文字コードに関する見識において私が尊敬する学者のひとりである)が,2009 年 12 月の段階で『新常用漢字表が迫るUnicode移行,『シフトJIS』では対応不可能』というコラムを ITpro に掲載し,新常用漢字表の改訂について IT 業界に警鐘を鳴らしていた。さすがである。

上で「いつもパソコンで入力している『叱』という文字のほうは誤字であって」と書いた。この記述そのものを訂正するつもりはないのだけれど,「叱」という字体はすでに一般に浸透しており,もはや「誤り」とするのは時代錯誤というものである。出版の世界でも「しかる」「しっせき」に対して「叱」を表記しても,「無知無教養」でも「恥ずかしい」わけでもない。きちんとした国語辞典,しかもコンピュータの利用が一般化する以前に出版された新潮国語辞典(昭和 40 年初版)でもすでに「叱る,呵る」となっている(ただし,私の手元にある昭和 58 年刊岩波書店『広辞苑』第三版では「𠮟」という正字体のみが掲載されているし,平成 14 年再版・大修館書店『新漢語新辞典』では正字体「𠮟」のみが見出しになっており「叱」字体については「叱(口を開くさま)は別字であるが,俗に混用する」という記事がある)。つまり,JIS 第一・第二水準における「叱」採用は,JIS が「間違った」というよりも,もとよりこうした俗化・混用の「定着」の反映と捉えるべきである。
 

※ 2011.5.28 付記

会社の Windows XP professional Version 2002, SP3 + IE 6.0.2900.5512 では「𠮟」がきちんと表示された。どのタイミングかのパッチで改善されたようである。

Babelbib with ptexlive

ptexlive で多言語文献参照パッケージ Babelbib を使ってみた。

LaTeX のマルチリンガルインタフェースとして Babel はデファクトスタンダードになっている。文献参照機能 BibTeX の世界でも Babelbib という Babel の言語切替に対応したパッケージが公開されている。Babelbib 2009 年 10 月の版でロシア語もサポートされた。ptexlive には初期導入されていて,すぐ使いはじめることができる。

pLaTeX2e においては BibTeX は jBibTeX を用いるのが主流だったが,ptexlive における日本語対応版は pBibTeX (pbibtex コマンド) という名称になっている。しかしながら,Babelbib を ptexlive で使う場合,pBibTeX ではなく BibTeX (bibtex コマンド) を使う。pBibTeX で文献データベース処理を行うとロシア語などでエラーになるからである。ptexlive ではキリル文字を欧文として扱う場合エスケープ形式 (文字を十六進数で表現した形式。例えば Ю^^d0^^ae で表す) にしなければならないが,pbibtex がこれをうまく処理できない。本家 bibtex ならうまく通るのである。

Babel を用いて ptexlive で日本語と外国語を混植するとき,稲垣さんの japanese.ldf 日本語言語定義を使っている人は多いと思う。Babelbib でも同様に japanese 環境に応じて文献参照の言語を切替えたい場合,Babelbib 用の日本語言語定義ファイル japanese.bdf を準備しなくてはならない。これの作り方はそんな難しいものではなく,すでにある言語定義をコピーして "Volume" などの文献項目表示ラベルを UTF-8 日本語で書直せばよい。私が試験的に準備した japanese.bdf を置いておく。ただし,これ,英語用を複製して japanese で動くようにしただけのしろもので,まったくラベルの書換をしていない。(※ 4.18 付記: nippon.ldf でも動くようにした。)

Babelbib の使い方は,端末から texdoc babelbib と打ち込めば表示される Babelbib PDF ドキュメントを参照していただきたい。ごくごく簡単に記すと,プリアンブルに以下のように書いておく。

\usepackage[utf8x]{inputenc}
\usepackage[T2A,T1]{fontenc}
\usepackage[italian,german,french,russian,english,japanese]{babel}
\usepackage[japanese]{babelbib}% load japanese.bdf

japanese.bdf は Babelbib 標準言語ではないので,パッケージ・オプションに japanese を指定してロードするよう指示しておくことがポイントである。

日本語 BibTeX では \bibliographystyle として jplainjabbrv が用意されているが,Babelbib では babplain などを使う。ただし,私が試した範囲では,ptexlive との組み合わせで,babplain, babplai3, babunsrt スタイル以外は動作しなかった。

サンプルを以下に示す。LaTeX 原稿 (mlbibjp.tex):

% -*- coding: utf-8; -*-
% mlbibjp.tex: BibTeX Babelbib test
% coded by isao yasuda.
\documentclass[b5paper]{jsarticle}
\usepackage[utf8x]{inputenc}
\usepackage[T2A,T1]{fontenc}
\usepackage[italian,german,french,russian,english,japanese]{babel}
\usepackage[japanese]{babelbib}
\setlength{\textwidth}{30zw}
\begin{document}
\begin{center}
  \Large\bf ptexlive Babelbib試験
\end{center}
 
Bib\TeX{}は\texttt{pbibtex}ではなく\texttt{bibtex}で実行した。
 
\vspace{1em}
ロシア語:
\selectlanguage{russian}
Юрий Лотман написал это в лит.\cite[с. 256]{lotman1970}.
 
\vspace{1em}
\selectlanguage{japanese}
イタリア語:
\selectlanguage{italian}
Nel 1930 venne pubblicato ``\textit{La carne, la morte e 
il diavolo nella letteratura romantica}'' (\cite{mario1930}), 
un testo che tradotto in inglese nel 1933 contribuirà 
ad estendere la sua fama in Gran Bretagna e Stati Uniti, 
e provocherà invece forti reazioni contrarie in Italia, 
fra cui quella di Benedetto Croce.
 
\nocite*
%% Choose one BibTeX style:
\bibliographystyle{babplain}% OK
%\bibliographystyle{bababbrv}% NG
%\bibliographystyle{babalpha}% NG
%\bibliographystyle{babunsrt}% OK
%\bibliographystyle{babamspl}% NG
\bibliography{ml}% see ml.bib
\end{document}

文献データベースファイル例 (ml.bib) は以下のとおり。書誌情報 language に Babel 言語名を指定すると,その言語でのマナーで文献出力がなされる。例にあるとおり,著者名,書名の文字列は UTF-8 で直接入力ができる。ロシア語もキリル文字をそのまま書いてよいし,独・仏語もアクセント付き文字をそのまま入力できる。

% -*- coding: utf-8; -*-
% ml.bib: babelbib test
% coded by isao yasuda.
@book{curtius1961,
  author={Curtius, E. R.},
  title={Europäische Literatur und lateinisches Mittelalter},
  year={1961},
  address={Berlin},
  language={german}
}
@inBook{mario1930,
  author={Praz, M.},
  title={La carne, la morte e il diavolo nella letteratura romantica},
  year={1930},
  address={Milano},
  language={italian}
}
@book{lotman1970,
  author={Лотман, Ю. М.},
  title={Структуры художественного текста},
  year={1970},
  publisher={Искусство},
  address={Москва},
  language={russian}
}
@book{tynyanov1969,
  author={Тынянов, Ю. Н.},
  title={Пушкин и его современники},
  year={1969},
  publisher={Наука},
  address={Москва},
  language={russian}
}
@Book{gracq1938,
  author={Gracq, Julien},
  title={Au château d’Argol},
  publisher={José Corti},
  year={1938},
  address={Paris},
  language={french}
}
@Book{basho1970,
  author={松尾 芭蕉},
  editor={中村俊定},
  title={芭蕉句集},
  publisher={岩波書店},
  year={1970},
  address={東京},
  language={japanese}
}

上記原稿をコンパイルし,PDF を生成するには以下のようにする。

% eplatex mlbibjp.tex
% bibtex mlbibjp.aux
% eplatex mlbibjp.tex
% eplatex mlbibjp.tex
% dvipdfmx mlbibjp.dvi

このとき注意が必要なのは,ロシア語,ギリシア語が原稿,文献データベースに含まれている場合,エスケープ形式にしておかないといけない点である。PTEX_IN_FILTER 環境変数にここであげたフィルタを指定して実行すれば,利用者が意識しなくても ptexlive (UNIX 版) がこの処理を通してくれる。

組版結果は以下のとおり。ここでは文献一覧をイタリア語環境で出力してみた。

20110409-babelbib.png

Unicode msearch 導入メモ

私のメインサイトでは namazu 2 でサイト内全文検索ができるようになっている。namazu 2 は UNIX 環境日本語全文検索エンジンの定番になって久しいが,基本的に ISO-2022-JP, Shift_JIS, EUC-JP の日本語文字コードを前提としており,多国語文書には適さない。ロシア語に限って言えば,Unicode ロシア語文書も検索できるのだけど(JIS の智慧のおかげ)。

最近ではサイト内検索エンジンをブログ管理システムに任せるか,Google のドメイン指定の検索窓を設けることで,自らインデックス管理自体をしないですむようになっており,独自に検索エンジンを設置する必要性は薄くなって来ている。ちなみに,Google のドメイン指定で自分のサイト内だけを検索するための検索窓は,以下のようなコードを自サイトのページに埋め込めばよい。

<form method="get" action="http://www.google.com/search">
  <input type="text" name="q" size="30" maxlength="255" value="" />
  <input type="hidden" value="検索対象ドメイン" name="as_sitesearch" />
  <input type="hidden" name="hl" value="ja" />
  <input type="hidden" name="ie" value="UTF-8" />
  <input type="submit" value="Google 検索" />
</form>
このサイト内なら—

私は受信したメールを MHonArc でアーカイブし,パスワードで文書セキュリティを保護することで,Web でどこからでも自分のメールを見られるようにしている。このような用途では,Google を使うわけには行かず(非公開なんだから当然),どうしても独自に検索エンジンを設置しなければならない。

Katsushi Matsuda,毛流麦花両氏による msearch は Unicode 文書からインデキシングが可能である。よって多国語文書も本来の文字列で検索が可能である。そこで私も Unicode 版 msearch Ver. 1.52 を導入してみた。これで柔軟な多国語検索とメールアーカイブ検索が両立できるというわけだ。http://yasuda.homeip.net/msearch.html から私のサイト内の検索ができるようにしてみた(メール検索は当然ながら非公開)。検索結果画面は msearch のお仕着せをそのまま使っている。まだ画面右上の namazu 検索は生きている。そのうち整理するつもりである。

msearch は,茶筌で日本語検索対象語を抽出してインデキシングを行う namazu とは異なり,おそらく n-gram で全文インデックスを抽出している。これは検索モレを防ぎかつ高速化を実現するのに有効な方法である。ただし,msearch は多国語文字を取り扱うことができるとはいえ,いわゆる語形変化に追随できるようなインデキシング解析をしているわけではなさそうである。Google なら,нести (「携える」という意味のロシア語不規則変化動詞不定形) を入力すると,несут (三人称複数現在) などの変化形をも検索できる。さすがである。

msearch は PDF, Microsoft Word, Microsoft Excel, Microsoft PowerPoint をインデキシングするためのフィルタを持たないため,そのままでは原則 HTML 文書だけを扱うことになる。私自身は .doc, .xls, .ppt ファイルをプライベートで扱うことはないので,まったく困らないのだが,PDF だけはなんとしても検索できるようにしたい。以下,PDF も取り扱うことができるように msearch 環境を調整するメモをしるしておく。環境は Mac OS X Snow Leopard (インデックス作成) 及び FreeBSD 8.1-RELEASE (公開 Web 環境) である。掲載内容は無保証である。

Unicode 版 msearch インストール

Unicode msearch のダウンロード,インストールは『サイト内全文検索エンジン ― Unicode版msearch』に毛流麦花氏による懇切丁寧な解説があるので,そちらを参照する。アーカイブを解凍し,cgi-bin/msearch にリソースを格納するだけである。Perl 5 が必要である。/~user/ などの Web ユーザディレクトリ下で msearch を運用する場合は,public_html/cgi-bin/msearch のパーミッションが 644 あるいは 755 (他ユーザの書込権限なし) になっていないと,Premature end of script headers のエラーが出て動作しないので注意。

私の場合,PDF 処理その他でサイトのドキュメント・ツリーを書き換えてしまうオペレーションが発生するため,Apache22 の公開エリアとは別に ~/var/webindex ディレクトリ (以下「ワークツリー」) を作成し,そのなかにサイト・ドキュメント・ツリーをコピーするとともに,msearch リソース・ディレクトリをも設置した。ここで検索インデックスをローカル作成(msearch では,Web ブラウザからインデックス作成操作が可能であるが,これを使わず,サーバ・ローカルの端末でコマンド操作によりインデックス作成を行うことを「ローカル作成」と呼んでいる)し,できたものを Apache22 の公開エリア /usr/local/www/apache22/cgi-bin/msearch にコピーするという運用である。

msearch のサイト用独自設定は default.cfg で行う。本稿の試行では,とりあえず set $home= の右辺を私のサイト URL に書き換えただけである。

PDF インデキシング

msearch で PDF ファイルのインデックスを作成するには,いくつか注意事項があり,少し工夫が必要である。そのままで PDF をインデックス作成対象に指定すると,インデックス・ローカル作成プログラム genindex.pl (本稿で「インデクサ」とあるのはこれのこと) は異常終了する。

PDF はテキスト変換した上でインデクサに掛けるのが基本である。UNIX X11 PDF ビュア XPDF のユーティリティ pdftotext で PDF — テキスト変換を行った上でインデキシング実行すればよい。このときテキスト化されたファイルを元の PDF ファイルと同じ名称にしておかないと,検索結果のリンクで当該ファイルを参照できないので,テキスト変換結果で元 PDF ファイルを上書きしておく必要がある。PDF 以外でも wvWare (Microsoft Word 用),xlHtml (Microsoft Excel 用),pptHtml (Microsoft PowerPoint 用) の各 UNIX ソフトウェアを利用し HTML ないしテキスト形式に変換することで,msearch インデキシングが可能である。

第二の注意点として,HTML / XML 以外のファイルに対して msearch は BOM で Unicode エンコードを判断していることがある。BOM がないと,Web 検索結果画面上の当該ヒットエントリの文字が化けてしまうのである。普通,UTF-8 でテキストファイルを作成するとき,BOM を付けたりしないので,インデクサに掛ける前に UTF-8 BOM (十六進コード "EFBBBF") をワークツリーのテキスト変換後ファイルの先頭に書き込んでおく。これは echo, cat コマンドなどで簡単にできるのだけれども,私は,PDF に混在した不要な制御コード文字を取り除く目的と合わせて,これを行う簡単なプログラム chkucntlchr を書いた。PDF 以外のテキストファイルも同様の処置が必要である。

以上の処理を自動で行うシェルスクリプトのコード例を以下に示す。これを含んで,msearch インデックス作成の全体シェルスクリプト例を最後に掲げてある。

# PDF format conversion
$WRK=ワークツリー
$UCK=chkucntlchr # 制御コード削除・BOM 付加ツール
$STP=タイムスタンプファイル (前回実行時の日付属性をもつ空ファイル)
echo "*  Convert PDF to TEXT by pdftotext (XPDF)."
cd $WRK
for i in `find -L . -newer $STP -name "*.pdf"`
do
    pdftotext -enc UTF-8 -nopgbrk $i $i.txt
    if [ $? -eq 0 ]; then
        echo "**  pdftotext $i OK."
        $UCK < $i.txt > $i
        if [ $? -eq 2 ]; then
            echo "**  $i is empty. Ignore."
            rm -f $i
        fi
    else
        echo "**  pdftotext $i NG. Ignore."
        rm -f $i
    fi
    rm -f $i.txt
done

また,chkucntlchr ツールの Perl コードは以下の通りである。

#!/usr/bin/perl -w
# -*- coding: utf-8; mode: cperl; -*-
# chkucntlchr
# 2011(c) isao yasuda.
# - delete words including control characters (U+0001--U+0020, U+007F--U+00A0)
# - Add UTF-8 BOM (x'efbbbf') for msearch indexer
# - Return code 0: normal; 1: suppressed; 2: imput empty;
use strict;
use utf8;
binmode STDOUT, ":utf8";
my $flg = 0;
my $lc = 0;
my $utf8_bom = "\xEF\xBB\xBF";      # BOM for UTF-8
utf8::decode($utf8_bom);
print $utf8_bom;
while (<STDIN>) {
    chomp($_); $lc++;
    utf8::decode($_);
    my @line = split(/\s/, $_);
    foreach my $wd (@line) {
        if ($wd =~ /[\x{0001}-\x{0020}\x{007F}-\x{00A0}]/) {
            $flg = 1;
        } else {
            print "$wd ";
        }
    }
    print "\n";
}
if (! $lc) {
    print STDERR "*** $0: input empty.\n";
    exit 2;
}
if ($flg) {
    print STDERR "*** $0: suppressed control characters.\n";
} else {
    print STDERR "*** $0: no problem.\n";
}
exit $flg;


segmentation fault 対策

msearch インデクサに PDF を食わせるに際して,もっとも悩んだのはテキスト化した PDF でも,インデクサが segmentation fault エラーで異常終了する場合があることであった。インデクサからコールされる indexing.pl に罠を仕掛けて調査したところ,問題が二つ判明した。

まず第一には,サイトのインデックス対象ファイル数が多いと,UNIX の ulimit のファイルオープン数の制限に引っ掛かり,異常終了してしまう。ulimit -a で制限値を確認し,インデックス対象ファイル数よりも open files の設定値が小さければ,ulimit -n 数値 で値をファイル数よりも大きな値に設定する。

第二には,msearch インデクサは正規表現 s 演算子によって HTML タグの除去処理を行っているが,これをすべてのファイルに適用しており,テキスト変換された PDF ファイル中の文字列如何によっては誤動作してしまう。私のサイトの PDF は,LaTeX 多言語文書を dvipdfmx で処理した生成物が多い。LaTeX フォント・パッケージの enc ファイル(エンコーディング定義ファイル)によってはヘンな文字の羅列になることがあり,これでタグ判定の正規表現がぶっとんでしまったらしい。indexing.pl 791 行目を,HTML / XML でないときは実行しないように,以下の改変を行うと,アボートしないようになった。

#    $contents =~ s/<(?:[^"'>]|"[^"]*"|'[^']*')*>/ /g;  # オリジナル
    $contents =~ s/<(?:[^"'>]|"[^"]*"|'[^']*')*>/ /g if ($html_xml); # 対策

XPDF 多国語化

XPDF pdftotext の多国語対応について簡単にしるしておく。FreeBSD Ports,Mac OS X MacPorts では XPDF Japanese port が用意されており,これをインストールすれば日本語 PDF については扱うことが出来るようになる。しかし,多国語 PDF はダメ。

XPDF のサイトでは中国語,ギリシア語などいくつかの言語設定追加リソース・パッケージが公開されている。これらを用いて多国語対応設定ファイルを作成する。以下のシェルスクリプトを実行すれば,パッケージをダウンロード・展開した上で,カレントディレクトリに xpdfrc を生成する。これを $HOME/.xpdfrc として格納する。次に,パッケージのリソース xpdf-japanese 等を XPDF 管理ディレクトリ (FreeBSD なら /usr/local/share/xpdf) にコピーする。これで,pdftotext により,日本語のほか簡体中文,繁體中文,韓国語,ロシア語,ギリシア語,アラビア語,ヘブライ語などの Unicode テキストが出力できるようになる。ただし,この実行で生成された xpdfrc には PDF 表示の際に必要となるフォント定義がないので,あくまで pdftotext テキスト変換用途と理解いただきたい。表示も含めた設定は XPDF ドキュメントを参照して displayNamedCIDFontTT 定義に好みの TrueType, OpenType フォントを指定するなりして調整してほしい(私は UNIX 環境での PDF 表示は Adobe Reader を使っているのでこのへんは放置している)。

#!/bin/sh
# Download XPDF language packs
WGET="wget -nH -nd "
XPDFSITE="ftp://ftp.foolabs.com/pub/xpdf"
# XPDFETC は xpdfrc があるディレクトリに変更する
XPDFETC="/usr/local/etc"
$WGET $XPDFSITE/xpdf-arabic.tar.gz
$WGET $XPDFSITE/xpdf-chinese-simplified.tar.gz
$WGET $XPDFSITE/xpdf-chinese-traditional.tar.gz
$WGET $XPDFSITE/xpdf-cyrillic.tar.gz
$WGET $XPDFSITE/xpdf-greek.tar.gz
$WGET $XPDFSITE/xpdf-hebrew.tar.gz
$WGET $XPDFSITE/xpdf-japanese.tar.gz
$WGET $XPDFSITE/xpdf-korean.tar.gz
$WGET $XPDFSITE/xpdf-latin2.tar.gz
$WGET $XPDFSITE/xpdf-thai.tar.gz
$WGET $XPDFSITE/xpdf-turkish.tar.gz
# Expand archives
for i in *.tar.gz; do tar zxvf $i; done
# make xpdfrc
cp $XPDFETC/xpdfrc .
for i in `find . -name "add-to-xpdfrc"`
do cat $i >> xpdfrc; done

インデキシング用シェルスクリプト

最後に私が自サイト用に作成したインデキシング用シェルスクリプトを掲載しておく。Mac OS X Snow Leopard 環境である。HTML ソース Subversion 管理エリアからワークツリーに更新ファイルをコピーし,ワークツリーでインデキシングしたのち,Mac 上の Apache22 cgi-bin 試験環境にインデックスファイル default.idx をコピーする。これでできた Mac 上の default.idx をそのまま FreeBSD サーバの msearch 環境に転送して公開するという運用形態である。

私は自サイトの HTML 等公開コンテンツを Subversion でバージョン管理しており,ソースを commit すると Subversion の commit スクリプトが動作して,自動的に更新コンテンツを Apache22 のドキュメント・ツリーにコピーするようにしている。このスクリプトを変更し,commit のタイミングで msearch インデクス作成スクリプトを呼び出して,検索インデクスを自動的に更新することもできる。crontab に登録して定時自動実行するのもよいと思う。

上記の PDF テキスト変換以外にも,HTML の iso-2022-jp to UTF-8 変換なども実行内容に含まれている。シェル内の各パス設定,サイト設定は私の環境そのままなので,もしこれを活用する方がいらっしゃるのなら,自分の環境に応じて書き換えないといけない。もちろん,中味をよく確認し,私のいい加減なコードにヘンなところがあれば手直しいただいたほうがよい。

#!/bin/sh
# -*- coding: utf-8; -*-
# msearch index generator for ISOLDE
# - coded by isao yasuda, 1 Apr. 2011
#
# DESCRIPTION
# -----------
# 1.前回タイムスタンンプより新しいページをSRCからWRKに格納する。
# 2.JISコードのページをUTF-8に変換して格納する。(nkf, sed)
# 3.PDFをテキスト変換してWRKに格納する。
#     rc OK: .pdf の内容はテキストファイル 
#     rc NG: .pdf, .pdf.txt を削除する
# 5.テキスト変換 OK のものの制御コードを削除する。(chkucntlchr)
# 4.WRKでインデックスを生成する。(genindex.pl
# 5.インデックスをWRKからPUBに格納する。
#
# CAUTION
# -------
# 1.初期作成時はファイル数が多いのでWRKにsite treeをコピーしておく。
#
 
# SRC: ページソースエリア
# WRK: ワークエリア
# PUB: 公開エリア (ここでは触らない)
WWW=/usr/local/www/apache22
SRC=/home/isao/src/noxinsomniae
PUB=$WWW/data
CGI=$WWW/cgi-bin/msearch
WRK=/home/isao/var/webindex/website
MSE=/home/isao/var/webindex/msearch
STP=/home/isao/var/webindex/stamp
TMP=/home/isao/var/webindex/tmp
UCK=$MSE/chkucntlchr
NKF=/usr/local/bin/nkf
PDFTOTEXT=/usr/local/bin/pdftotext
 
echo "********************************************************"
echo "*  msearch Index Generation Start `date '+%Y/%m/%d %H:%M:%S.'`  *"
echo "********************************************************"
# Copy SRC to WRK
echo "*  New Files Archiving."
cd $SRC
find -L . -newer $STP -type f | grep -v '.svn' |\
xargs tar cf - | ( cd $WRK; tar xvf - )
cd $WRK
 
# Convert iso-2022-jp to utf-8
echo "*  Convert ISO-2022-JP pages to UTF-8 pages."
cd $SRC
for i in `find -L . -newer $STP -name "*.html"`
do
    $NKF -w $i |\
    sed -e 's|charset=[Ii][Ss][Oo]-2022-[Jj][Pp]|charset=UTF-8|g' > $WRK/$i
done
 
# PDF format conversion
echo "*  Convert PDF to TEXT by pdftotext (XPDF)."
cd $WRK
for i in `find -L . -newer $STP -name "*.pdf"`
do
    $PDFTOTEXT -enc UTF-8 -nopgbrk $SRC/$i $i.txt
    if [ $? -eq 0 ]; then
        echo "**  $PDFTOTEXT $i OK."
        $UCK < $i.txt > $i
        if [ $? -eq 2 ]; then
            echo "**  $i is empty. Ignore."
            rm -f $i
        fi
    else
        echo "**  $PDFTOTEXT $i NG. Ignore."
        rm -f $i
    fi
    rm -f $i.txt
done
 
# add BOM to text files
echo "*  Add BOM to TEXT files."
cd $WRK
for i in `find -L . -newer $STP -name "*.txt" -or -name "*.tex"`
do
    $UCK < $i > $i.tmp
    if [ $? -eq 2 ]; then
        echo "**  $i is empty. Ignore."
        rm -f $i
    fi
    mv $i.tmp $i
done
 
# rm svn control
cd $WRK
find . -name ".svn" -or -name ".#*" | xargs rm -fr
 
# Indexing
echo "*  Execute Indexing by genindex.pl. PARAM:"
echo "*   1 インデックス名前:        default"
echo "*   2 インデックス対象DIR:     $WRK"
echo "*   3 インデックス対象URL:     http://yasuda.homeip.net/"
echo "*   4 インデックス対象拡張子:   .html,.txt,.pdf,.tex"
echo "*   5 非インデックス対象DIR:   admin,common,css,archives"
echo "*   6 非インデックス対象拡張子: (指定無し)"
echo "*   7 非インデックス対象KWD:   (指定無し)"
echo "*   8 ランキング方法:         最終更新日時降順(1)"
echo "*   9 alt属性の文字:         指定しない(0)"
cd $MSE
./genindex.pl <<EOM
default
/home/isao/var/webindex/website
http://yasuda.homeip.net/
.html,.txt,.pdf,.tex
admin,common,css,archives
 
 
1
0
EOM
 
if [ $? -eq 0 ]; then
    echo "*  Index Generation Succeeded."
    ls -l $MSE/default.idx
    echo "*  Now Copy default.idx to $CGI."
    cp -p $MSE/default.idx $CGI
    # STAMP modify
    SDT=`ls -l $STP`
    echo "*  Previous Update: $SDT"
    touch $STP
    SDT=`ls -l $STP`
    echo "*  Now Updated:     $SDT"
else
    echo "*  Index Generation Something bad."
    ls -l $MSE/default.idx
fi
 
echo "*  Procedure Ended. `date '+%Y/%m/%d %H:%M:%S.'`"
# end of script

Year 2038 Problem, UNIX Millenium Bug

UNIX 2038 年問題というのがある。西暦 2000 年問題と同じような話である。伝統的な UNIX オペレーティングシステムは日付・時刻を 32 bit 整数値で管理しており,1971 年 1 月 1 日 0 時 0 分 0 秒をはじまりとして 1 秒毎に数値を 1 ずつ増加させる。これを前提としてそのときの時刻を計算している。ところがこの 32 bit データが 2038 年 1 月 19 日 3 時 14 分 07 秒を過ぎると桁溢れし,なんと 1901 年 12 月 13 日 20 時 45 分 52 秒 (UTC) にすっ飛んでしまう。2038 年問題とは,これに伴って UNIX システムでさまざまな不具合が出るであろう事態の総称である。

2038 年なんてまだまだ先だと思う人がいるだろう。でももうこの問題は現実味を帯びはじめている。私は会社で回覧されてくる他サイトの事故事例には必ず目を通すようにしているが,先日,HP-UX (ヒューレットパッカード社の UNIX) の旧版を使っているサイトでユーザ・ログインができなくなる障害報告を読んだ。ユーザ管理項目に有効期限があり,それに 9999 日みたいな大きな数値を指定したら,有効期限日時が 2038 年を飛び越えて 1901 年から折り返してしまい,有効期限はすでに超過しているとシステムが判断したがゆえの障害だった(ヒューレットパッカード社の名誉のために付け加えておくと,HP-UX 最新版では 2038 年問題は訂正されている)。2038 年問題は世間ではあんまり騒がれていないけれども,UNIX サーバに移行したクリティカルなシステムが極めて多いだけに,これ以上にマズイことも起きる可能性がないわけではない。

西暦 2000 年問題は,多くのシステムが西暦を下 2 桁で管理しているがゆえに 2000 年の判断を 1900 年と誤ってしまうことに起因した。その発現は予想が一筋縄ではなく,大陸間弾道弾が誤動作するかも知れないとか,ジェット機が操縦不能に陥るとか,大いに世間を騒がせた。おまけに 2000 年は 100 でも 400 でも割り切れるので超特例的閏年であったという事情が,計算機関係者の恐怖を煽り立てた。閏年を「4 で割り切れ,100 で割り切れない年」とする単純なロジックを使うプログラマが実際にいたのである。ふつう 400 年に一回のことを人生で突き詰めて考える人はまれである。関ヶ原の合戦以来のことが自分の人生で起こるなんて考える人はまれである。

計算機業界のことを知らない人には,2000 年なんてすぐ来るのがわかっているのになんでこんなバカな設計にしたんだろう,まったく呆れる,のようなことをホザく正論吐きがゴマンといた。現象の内在的論理を辿るよりも前に己の感じ方に満足してしまう人,進化の恩恵に無意識に浴する人の典型である。私の尊敬する米原万里も,どの本であったか忘れたが,同じことを書いていて,私は正直悲しくなったことを思い出す。もちろん西暦 1990 年代に設計され,それなりの期間使用される予定のシステムならば,2000 年を考慮していないのはただのバカである。しかし,当時問題になったのはコンピュータ黎明期から少しずつ改修されながら大規模化したシステムだからこそであった。共同体への影響がじつに大きい官庁システムがまさにこれにあたるからだった。私の顧客は,幸いにも,その事情を痛いほど知っており,2000 年対応システム改修にケチケチしなかった。

そもそも,2000 年で問題が出るのがおよそわかっていながら,なんでそういう設計になっていたのか。コンピュータ機器の進化には,3 年で性能が 2 倍になるという「ムーアの法則」と呼ばれる経験則がある。2011 年から逆算して 70 年代あたりに舞い戻ると,かつての計算機が現在と比べていかに貧相なものだったかが想像できるだろう。30 年昔の計算機の性能はいまの 210 分の 1,逆にいうと同じもののお値段はいまの 210 倍。40 年前なら 213 倍くらい。そう,その当時はメモリ 256KB の一月の借料がサラリーマン大卒初任給を越えるくらい高価だった。磁気ディスクも同じ。こういう超高価なリソースを使うとき,とにかくケチろうとするのは当然である。日付・時刻はどんな業務データの属性にも付いて回り,これを仮に short (2 byte) 整数で年,月,日,時,分,秒 12 byte 使えばデータに占めるその割合はバカにならない(まさかと思う前に 12 × 213 がどれほど重いか考えるがよい。いま日付・時刻の格納に一個あたり 12 × 213 byte 使わせてもらいます,なんて顧客に言ったら即刻クビである)。昔,私の担当したシステムでは西暦を下 2 桁だけで管理し,数字 1 桁を 4 bit に入れて 6 byte に切詰めていた。理論的にはもっと切詰められるが計算速度やわかり易さとの兼ね合いでこうしていたわけだ。それくらい記憶域が貴重だったのである。70 年代くらいのシステム設計者は,おそらく皆,計算理論の前に経済学に縛られていたのだと思う。

UNIX 2038 年問題についても,なんでたった 32 bit で管理するなんてケチったんだろうと思う人がいまならウヨウヨいるはずである。4 byte 32 bit というデータ構造は 32 bit 計算機がもっとも高速に取り扱うことのできるものなのだ。でも,それを抜きにしても,1970 年ごろの UNIX 設計者は,2038 年にはボクはもう生きていないと無意識に思ったはずだ。人生 70 古来稀なり。そのころにはボクたちのような貧しい資源制約から解放された,もっと夢のようなオペレーティングシステムが動いているさ,と。つまり計算機の世界でも,老人の感慨同様,人生はあっと言う間に過ぎたわけである。
 

* * *

ところで,西暦 2000 年問題はジョークのネタにもなっている。「2005 年のある日,アルバニア軍のコンピュータ系統が一斉にダウンした。その理由は? — 西暦 2000 年問題」。その国の時代遅れを笑う格好の題材になったんである。でも 2000 年を大きく超過して忘れ去られたころにプチ 2000 年問題が出て恥ずかしい思いをした SE/プログラマは必ずいると私は思う。

携帯機種変更

今日,会社携帯電話の機種変更をした。これまで使っていたのは日立製 W22H というモデルで,斜めにスライドする珍しい一品だった。電池交換を一度しただけで,まる 6 年も使ったんである。その間に何回も取り落としたりしてカメラレンズ枠が破損してレンズがむき出しになってしまっていたが,それはそれでスケルトンの味が出て愛用していたんである。古い機種とはいえメールはもちろん Ezweb も利用でき,機能的にはなんの不満もなかったわけだけど,2012 年 7 月の 800 MHz 帯周波数再編に関連して IC カード未対応の CDMA 1X がそろそろ使えなくなるということで,新機種移行ということになったんである。
 

20110306-keitai.png

新しい携帯電話もやはり日立製。beskey という名前である。会社が勝手に白色モデルを選定していた(白は経年のヘタレ感がすぐ出てしまうので,私は黒がよかったのだけど,そこは会社携帯,注文をつけても詮ないこと)。私もはじめて二つ折りの流行デザインモデルを携帯することになったわけである。ワンセグが観られるようになった。なんと 8.1 Mega 画素カメラを内臓している。でも,こんなのはどうでもよい。ま,機能的にはなんの新たな期待もなかったのであるが,この際なので,イヤホン,USB ケーブル,microSD カードを自前で調達して音楽でも入れて遊ぼ,ということに。LISMO Port を妻の Windows 7 PC に入れこんで(なんで Mac で使えるようにしてくれないんだ? あ,LISMO は Apple の 宿敵 SONY 製だからか),CD をラッピング,転送してみた。CD ジャケットも表示されてなかなかである。Apple iPod はかさばるので会社にもって行く気がしない。私はサラリーマンのくせに鞄をもたず手ぶらをするたちで,スーツの内ポケットに手帳と財布,外ポケットに通勤定期と文庫本を入れると,もう iPod の余地がない。この携帯ならイヤホンだけポケットに忍ばせておけばよろしい。 息子にも LISMO で携帯に音楽を転送する方法を教えてやったら(「着うたで浪費すんな!」という意味で),えらく感謝されてしまった。
 

20110318-lismo.png

Windows 7 マシン

妻が使っている PC はもう 10 年くらい昔に買ったオンボロ。JCS Vintage という渋いブランドマシンである。よっていまだに Windows 2000。IE6 は最近の新しいページを表示すると「スクリプトエラー,デバッグしますか」のエラーメッセージが煩くて仕方ない。ディスクも一杯でしょっちゅう不要データを削除しなければならないし,データのフラグメンテーションがハンパでないのにデフラグをする空きすら確保できず,低速運転でガマンしなければならなかった。というわけで,そろそろ買い替えようということになった。Mac にしたらと勧めたが,妻は「また新しいこと覚えんのイヤ」ということでやはり窓族を通すことに。

キューブ型静音設計の PC をネットで購入した。Shuttle 社が製造しているベアボーンである。モニタ,キーボード,マウス,スピーカはこれまでの品物を継続使用する。39,000 円しかしなかったが,それでも intel Core2Duo E7500/2.93 GHz MPU,2 GB DDR-3 メモリ,500 GB HDD,24 倍速スーパマルチドライブ搭載の充分な性能を持つ。妻の機械はわが家で最高性能の PC となった。Windows 7 はネットオークションで Ultimate 32/64 bit 同梱パッケージ正規品を安値で手に入れた。 このモデルは 35 言語から好きな言語を選択できるんである。もちろん日本語を選んだ。

このブログ・システムを稼働させている FreeBSD サーバ(同じ Shuttle 製キューブ。こちらは Core2Duo E7200/2.53 GHz)の上に新 PC を置いた。とりあえず 32bit 版をインストールし,リモートプリンタ,Thunderbird メールクライアント,秀丸エディタ,Adobe Reader 10,Silverlight だけは設置・調整した。いまの Windows は IP ネットワーク設定を自動でやってくれるのに感心した。Office は必要になったら設置するということにした。ところで,Windows 7 って Microsoft Office 2000--2003 は動くんだろうか。Office はアップグレードパッケージでこれまで繋いで来たので,アタマから入れ込むのはチョー面倒である。500 GB もディスクがありゃ昔なら FreeBSD か Linux も別パーティションにインストールして遊んだであろうが,いまはもうそんな元気はない。
 

20110305-shattle.png

misima 漢詩分析・詩語検索機能について,簡易版を追加した。旧版は SQL を直接入力する形態だった。これは DB に蓄積しているすべての項目について,柔軟な検索ができるのだけれども,SQL 操作ということもあり,限定公開とした。簡易版はごく限られた条件指定しかできない。それでもこれくらいありゃいいか,ということでこちらは一般の漢詩作成者の方々のお役に立てればと思い,一般公開とする。旧版も expert 版ということで残してある。方式的には expert 版も簡易版も同じで,簡易版は SQL 組立ての JavaScript を追加しただけである。

開発・本番環境は FreeBSD 8.0-RELEASE, Mac OS X Snow Leopard, Tiger, J2SDK-1.5, J2SDK-1.6, Tomcat-5.5, DWR-3-rc1, SQLite-3-5.6, SQLite-JDBC-3.7.2。ブラウザは Safari-5.0.3, Safari-4.1.3, Google Chrome-9.0 にて確認している。Windows ではまったく試験していないのでちょっと不安。

ここで簡易版の使い方を簡単にしるしておく。misima 漢詩分析・詩語検索機能のコンセプト,実現方式については『misimaKansiServlet 漢詩 Chinese Verse 平仄音韻分析』,『DWR with Java: misima 漢詩詩語検索』を参照。

misima 漢詩平仄音韻分析(Servlet版)』ページの「詩語検索」欄にある をクリックすると,別ウィンドウで検索画面が開く。検索はここで実行する。検索条件は図 1. に示す「平仄パターン」,「詩語」,「韻字」,「韻目」 を指定できる。
 

20110220-condition.png

図 1. 検索条件

「平仄パターン」(A) は求める詩語の平仄の並びである。プルダウンで平仄を設定する。「」は「平」,「」は「仄」を意味する。初期表示では「○-○-○」,すなわち「平-平-平」の 3 文字詩語の条件になっている。もし 2 文字の詩語を求めたいなら,3 番目のプルダウンで「-」を選択しなければならない。

○,●」いずれの場合も,検索結果に「」記号のものが含まれる。これは両韻を示す記号である。例えば,「思」という文字は意味によって「上平聲四支」と「仄去聲四寘」と平・仄どちらでも使用される可能性をもつ。もちろん詩語においてはどちらかに決まるのであるが,misima 平仄辞書で両韻のものは平・仄いずれの条件でもヒットするようになっている。だから当該詩語でその両韻文字がどちらの平仄で用いられているのか,辞書に当たって意味を確認する必要がある。

「詩語」(B) は指定した一文字を含む条件である。一文字しか指定できない。

「韻字」(C) 指定は詩語の末尾文字の条件である。押韻文字が決まっているときに用いる。

「韻目」(D) は「一東」などの韻目で検索するための指定である。押韻文字が決まっているとき,その韻目を含む詩語を閲覧したい場合などに用いる。韻目指定は面倒なので,ポップアップから選択するようになっている。「韻目」の入力エリアにカーソルが位置づけられると,入力メニューがポップアップされる(図 2.)。その一覧から検索したい韻目をクリックすると,入力エリアに韻目条件が設定され,メニューが消えるようになっている。
 

20110220-inmoku.png

図 2. 韻目ポップアップメニュー

条件 (A) は必須である。かつ,(B),(C),(D) から少なくともひとつは条件指定をしなければならない。そして,検索は (A) and ([(B)] or [(C)] or [(D)]) ([ ] 内はひとつ以上必要) という条件で実行される。これは長大な検索結果を防ぐためである。簡易検索はこれ以外の条件指定はできない。

簡易版検索結果例を図 3. に示す。これは「●-○-○」の 3 文字でかつ韻字が「秋」である詩語の検索条件である。
 

20110220-simple-search.png

図 3. 簡易版検索結果例

詩語韻目は,「ニモニック:番号:韻目」の様式で出力される。ニモニックの意味は次の通りである。hk: 上平聲; hs: 下平聲; sj: 仄上聲; sk: 仄去聲; sn: 仄入聲。「hk:01:東」は「上平聲一東」を意味する。; (セミコロン) は当該文字が複数の韻目を有することを示している。A;B;C は A, B, C 三つの韻目を持つ文字であることを示す。対応する文字の区切りは - (ハイフン) で判断できる。

簡易版では,(A) and (B) and ((C) or (D)) などのような検索条件指定は不可である。こういう柔軟な条件設定は expert 検索でのみ可能である。expert 版の条件指定は,「実行 SQL」のテキストエリアに where 句の内容を直接コーディングする。検索キーはページにある通り,詩語: sg; 詩語平仄: hs1; 詩語韻目: in1; 押韻字: oj; 押韻字平仄 hs2; 押韻字韻: in2; 詩語字数: ss; 出典: ta が指定できる。expert 版の検索結果を図 4. に示す。
 

20110220-expert-search.png

図 4. expert 版検索結果例

 

最後に参考文献を上げておく。
 

Ajax の高度な活用集。詩語検索機能で利用した DWR についても,サンプル付で少し解説されている。しかし,addRows() 関数の仕様など,DB 検索結果表示に適したテクニックの解説までは望むべくもなく,少し自分の課題に応じた詳細事項は,やはり DWR サイトのドキュメントに当たらないと解決不可能である。
 

オライリーのクックブック・シリーズは困ったときに必ず役に立つ。本書で述べられている便利なライブラリもオライリーのサイトからダウンロードできる。
 

これは JavaScript のリファレンス本としてはいちばん優れていると思う。なんといっても,非同期通信オブジェクト,XML DOM オブジェクトについて整理されている。オライリーの『クイックリファレンス』よりも個人的には使いやすい。
 

※ 2011.12.23 付記
Spam が多いので misimaKansiServlet は限定公開としました。悪しからず。

DWR with Java: misima 漢詩詩語検索

漢詩作成支援の一環で今回,詩語検索を追加した。SQLite3 で構築した詩語データベースを Java サーブレットから JDBC でアクセスする。高速化のために Ajax + Java Servlet を基本とした。近年,Ajax も便利なライブラリが出現し,Web 2.0 の世界をより簡単に構築できるようになった。今回はそのひとつ DWR (Dynamic Web Remoting) を使うことにした。

DWR の特徴は大きく二つある。第一に,JavaScript 動作の微妙に異なる数あるブラウザへの対応を吸収してくれること。第二に JavaScript からサーバの Java クラスの呼び出しを可能としたこと。とくに後者は,非同期通信インタフェースとサーバ (Servlet) の存在をプログラマから隠蔽することにより Java Servlet の約束事に精通していない Java プログラマにも (JavaScript が書けないとならないんだけど) Web サーバ・プログラミングを近しいものとする。

以下,misima 漢詩詩語検索を作成したメモを残しておく。

詩語データベースの構築

太刀掛重男著『詩語完備 だれにもできる漢詩の作り方』は,詩語に基づいて漢詩を作成する方法を述べており,合わせて詩語集を掲載している。また,詩語の韻書としては『詩韻含英異同辨』という決定版が存在する。できればこうした詩語集をデータベースに蓄積するのがよいのだが,著作権問題があり公開 DB に入れるのは憚られる(自分だけの個人辞書として構築するならよいのだろうが)。しかも,データ入力に気の遠くなるような時間を要する。私のようなサラリーマンにはとうてい不可能である。

そこで今回の詩語 DB は,どちらかというとコーパス・ビュア風のアイデアに基づくことにした。つまり,インターネットで大量に公開されている漢詩テクストを掻き集め,その詩行を 2 文字 + 3 文字 (五言詩) や 2 文字 + 2 文字 + 3 文字 (七言詩) に分解し,それぞれを「詩語」と見なし,これらに平仄・音韻分析処理を加えて,その結果をリレーショナル・データベースに仕立て上げるわけである。平仄・音韻分析用のデータベースは,『misima 漢詩平仄音韻分析』で構築済みだった。

私はロシア語作文をする際,語結合が妥当かどうか Google で検索してみる。ロシア語は「〜において」というとき,в, на のいずれの前置詞を使うのか悩ましいことが多い。「お茶会で」は на чайной церемонии でよいのか? Google で検索するとこのフレーズを含むきちんとしたページがたくさんヒットするので,不自然ではないとハッキリする。コーパス・ビュア風というのはこういう使い方を指している。漢詩詩語についても過去の偉大な詩人の作品で同じような確認ができるはずである。もちろん,詩語の意味,題詠用の分類(秋思,離別などの詩語と結びついたテーマ論)がないと片手落ちなんであるが,求める平仄,文字,字韻とそれに合致する詩語にどのようなものがあるのかが調査でき,詩語検索機能としてはそれなりのものが得られるはずだと考えた。「平-平-仄のパターンでこの文字を含む詩語」,「脚韻がこの韻目の詩語」,程度でも検索できれば便利ではないか?

『唐詩選』などの漢詩 500 首近くをネットから wget で戴いて来て,Perl HTML::TreeBuilder モジュールでもって詩テクストを解析,抽出して約 4,000 語の詩語テクストを得た。ついでに出典(詩人,題名)情報などの属性をぶら下げた。これに詩語の平仄データ,韻目などを追加して SQLite3 のデータベースが成った。

Java 詩語検索クラスの作成

DWR は様々な Java クラスの呼び出しをサポートしている。今回は JavaBeans で行くことにした。JavaBeans はデータを保持するオブジェクトで,データ項目の出し入れに set〜, get〜 なるメソッドを一式用意しておくというモジュール構造であり,データ部品化の基本になっている。サーバで DB を検索し,その結果を Bean に入れ,JavaScript から DWR インタフェースで取り出す。基本設計はそのようなものである。詩語のための JavaBeans SigoBean.java を以下に示す。本来なら DB に蓄積している項目はいくつもあるが,ここでは詩語,平仄,韻目のみに限定してある。

// -*- coding: utf-8; mode: java; -*-
/**
 *  misimaKansi SigoBean 漢詩詩語
 *  Copyright(c) 2011, isao yasuda, All Rights Reserved.
 */
public class SigoBean
{
    /**
     *  SQLite3 database
     *  詩語 sg, 詩語平仄 hs1, 詩語韻 in1
     */
    private String sg  = null; // 詩語
    private String hs1 = null; // 平仄
    private String in1 = null; // 韻目
 
    /** Constructor */
    public SigoBean(String sg,  // 詩語
                    String hs1, // 平仄
                    String in1) // 韻目
    {
        this.sg  = sg;
        this.hs1 = hs1;
        this.in1 = in1;
    }
 
    /** Getter メソッド */
    public String getSigo()    { return sg;  }
    public String getHyosoku() { return hs1; }
    public String getSigoIn()  { return in1; }
 
    /** Setter メソッド */
    public void setSigo(String sg)     { this.sg  = sg;  }
    public void setHyosoku(String hs1) { this.hs1 = hs1; }
    public void setSigoIn(String in1)  { this.in1 = in1; }
 
    /** テキスト取得用メソッド */
    public String getSigoBean(String sep) {
        return sg + sep + hs1 + sep + in1;
    }
}

さて,実際に詩語データベースを検索し,上記詩語 Bean の配列テーブルとして結果をストアする Java クラスが当然必要である。SigoTable.java コードを以下に示す。ここで,SQLite3 DB アクセスは JDBC インタフェースを使っている。SQLite 用 JDBC ドライバは SQLiteJDBC – Xerial – Trac から sqlite-jdbc-3.7.2.jar (2010.8.27 版) を落として,クラスパスに通しておくとともに,Servlet コンテナ Tomcat の $CATALINA_HOME/common/lib/ にもコピーしておく。

// -*- coding: utf-8; mode: java; -*-
import java.util.ArrayList;
import java.util.List;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
 *  misimaKansi SigoTable 漢詩詩語検索
 *  Copyright(c) 2011, isao yasuda, All Rights Reserved.
 */
public class SigoTable
{
    /** 詩語 DB path */
    private static final String dbpath = "/usr/local/etc/misima/SIGO.db";
    /** SQL */
    private String sqlcond = null;
    /** 詩語テーブル行データ */
    private ArrayList<SigoBean> dbRowList = null;
    /** データ行数 */
    private int gyosu;
   
    /**
     * 詩語 DB を検索し,詩語テーブルを SigoBean 配列として返却する
     * @return 詩語テーブル
     */
    public SigoBean[] getSigoTable(String cond) throws Exception {
        sqlcond = "select * from SIGOTBL where " + cond + ";";
        dbRowList = new ArrayList<SigoBean>();
        /** DB検索 */
        Class.forName("org.sqlite.JDBC");
        Connection conn = null;
        try {
            // create a database connection
            conn = DriverManager.getConnection("jdbc:sqlite:" + dbpath);
            Statement sttmnt = conn.createStatement();
            sttmnt.setQueryTimeout(30); // set timeout to 30 sec.
            ResultSet rset = sttmnt.executeQuery(sqlcond);
            while(rset.next()) {
                // read the result set
                dbRowList.add(new SigoBean(rset.getString("sg"),
                                           rset.getString("hs1"),
                                           rset.getString("in1")));
            }
            rset.close();
        }
        catch(Exception e) {
            System.err.println(e.getMessage());
        }
        finally {
            try {
                if(conn != null)
                    conn.close();
            }
            catch(Exception e) {
                // connection close failed.
                System.err.println(e);
            }
        }
        gyosu = dbRowList.size();
        SigoBean[] dbrows = new SigoBean[gyosu];
        return dbRowList.toArray(dbrows);
    }
   
    /**
     * 詩語 DB 行数を返却する (for debug)
     * @return 行数
     */
    public int size() {
        return gyosu;
    }
}

DWR 環境の作成

DWR のパッケージは DWR ダウンロードページ から dwr.war (Version 3. rc-1) を落として使った。DWR のサーバ側環境設定は大きく 3 点。以下簡単に示す。

  1. ダウンロードした dwr.war を詩語検索 Web アプリの WEB-INF/lib ディレクトリ直下に格納する。
  2. dwr.xml を,以下の内容で WEB-INF/ 下に格納する。これは JavaScript の記述とサーバ側 Java クラスを結びつけるための記述である。create タグで上記 SigoTable クラスが呼び出されるよう,convert タグによってそのデータ構造へのアクセスは JavaBeans SigoBean を介して行われるよう指定している。詳細は Configuring dwr.xml を参照のこと。
  3. <!DOCTYPE dwr PUBLIC
        "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN"
        "http://directwebremoting.org/schema/dwr30.dtd">
    <!-- -*- coding: UTF-8; -*- 
        misima 漢詩詩語検索用 dwr.xml
        Copyright (c) 2011, isao yasuda, All Rigths Reserved.
    -->
    <dwr>
      <allow>
        <create creator="new" javascript="SigoTable">
          <param name="class" value="SigoTable"/>
        </create>
        <convert converter="bean" match="SigoBean"/>
      </allow>
    </dwr>

  4. 詩語検索 Servlet 用のデプロイメント・ディスクリプタ web.xmlWEB-INF/ 下に準備する。今回,旧字・旧仮名遣い変換 misimaservlet のサブシステムとして作成したので,すでにある web.xml に DWR 関連用の servlet タグ及び servlet-mapping タグを追加した。DWR 部分は以下とまったく同じでよいと思う。詳細は DWR WEB-INF Reference を参照のこと。
  5. <!DOCTYPE web-app
        PUBLIC  "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
        "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
    <!-- -*- coding: UTF-8; -*- 
        misimaServlet web.xml デプロイメントデスクリプタ
        Copyright (c) 2007, isao yasuda, All Rigths Reserved.
    -->
    <web-app>
      <!-- misimaservlet サーブレット -->
      <servlet>
        <servlet-name>misimaServlet</servlet-name>
        <servlet-class>misimaServlet</servlet-class>
    ... (略) ...
      </servlet>
     
      <!-- DWR サーブレット -->
      <servlet>
        <servlet-name>dwr-invoker</servlet-name>
        <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
        <init-param>
          <param-name>debug</param-name>
          <param-value>true</param-value>
        </init-param>
      </servlet>
     
      <!-- misimaservlet サーブレットのマッピング -->
      <servlet-mapping>
        <servlet-name>misimaServlet</servlet-name>
        <url-pattern>/convert</url-pattern>
      </servlet-mapping>
      
      <!-- DWR サーブレットのマッピング -->
      <servlet-mapping>
        <servlet-name>dwr-invoker</servlet-name>
        <url-pattern>/dwr/*</url-pattern>
      </servlet-mapping>
    </web-app>

クライアント・ページ JavaScript の作成

最後に DWR のキモであるクライアント側 JavaScript を作成する。サービス用 HTML から呼び出す JavaScript として DWR ライブラリを次のように指定しておく。

<script type="text/javascript" src="dwr/engine.js"></script>
<script type="text/javascript" src="dwr/util.js"></script>
<script type="text/javascript" src="dwr/interface/SigoTable.js"></script>

はじめの 2 行はおまじないのようなものである。dwr/interface/SigoTable.js は,dwr.xml における javascript="SigoTable" という指定を受けて DWR が自動生成するスクリプトであり,ユーザアプリに応じて名前を変えて指定しなければならない。

さて,DB を検索し,その結果が挿入される HTML は以下の通りである。input タグに SQL 条件を入力し,「検索」ボタンを押下すると,retrieveSigo() 関数(後述)を起動するコードになっている。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
          "http://www.w3.org/TR/html4/loose.dtd">
<!-- Copyright(c) 2011, isao yasuda, All Rights Reserved. -->
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>misima 漢詩詩語検索</title>
  </head>
  <body>
    <dl compact>
      <dt>実行 SQL</dt>
      <dd>select * from SIGOTBL where <br> 
        <input type="text" size="70" id="condition" />;
        <input type="button" value="検索" onClick="retrieveSigo();" />
      </dd>
    </dl>
    <!-- 詩語検索結果挿入するテーブル -->
    <table border="0">
      <thead>
        <tr style="background-color: #ccc;">
          <th width="80px">詩語</th>
          <th width="120px">詩語平仄</th>
          <th width="400px">詩語韻</th>
        </tr>
      </thead>
      <tbody id="sigotable"></tbody>
    </table>
    <script type="text/javascript" src="dwr/engine.js"></script>
    <script type="text/javascript" src="dwr/util.js"></script>
    <script type="text/javascript" src="dwr/interface/SigoTable.js"></script>
    <script type="text/javascript" src="./sigo.js"></script>
  </body>
</html>

検索ボタンに紐づけられた検索実行制御処理 retrieveSigo() 関数は以下の通り。上記 HTML の sigo.js にこれを定義しておく。ここでサーバとの非同期通信を担っているのは SigoTable.getSigoTable(sql.value, ...); の部分である。サーバ側にある SigoTable クラスの getSigoTable() メソッドをコールしている。これは Java クラスを操作するのと同様の様式であり,DWR の特徴となっている。このコーリング書式は クラス名.メソッド名(メソッド引数, コールバック関数); である。サーバで実行されたメソッドの返値を引数として コールバック関数 が実行される。このなかでブラウザ表示に必要な処理を記述する。

// -*- coding: utf-8; mode: javascript; -*- 
// misima 漢詩詩語検索
function retrieveSigo() {
    var sql = document.getElementById("condition").value;
    var cellFuncs = [
        function(data) { return data.sigo; },    // 詩語
        function(data) { return data.hyosoku; }, // 平仄
        function(data) { return data.sigoIn; }   // 韻目
    ];
    // insert table contents from DB    
    SigoTable.getSigoTable(
        sql,
        function(data){ // callback
            dwr.util.removeAllRows("sigotable");
            dwr.util.addRows("sigotable", data, cellFuncs, {
                // 偶数行の背景色を変更
                rowCreator:function(options) {
                    var row = document.createElement("tr");
                    if (options.rowIndex % 2 != 0) {
                        row.style.background = "#cccccc";
                    }
                    return row;
                },
                escapeHtml:false 
            });
        });
}

retrieveSigo() 関数の引数のなかに関数が記述されておりそこでさらに関数が入れ子になっている。わかりにくいのでもう少し説明しておく。ここでサーバからの返値は function(data) 関数 (コールバック関数) に渡され,こちらは DWR util.js のユーティリティ addRows() 関数で HTML のテーブル (上記 HTML の <tbody id="sigotable"> の位置) にデータを展開している。addRows() 関数の書式は以下の通りである。

dwr.util.addRows(id, array, cellfuncs, [options]);
- id: テーブル要素の id。テーブル要素は <tbody id="id"> とするのがよい。
- array: 表にしたいデータ配列
- cellfuncs: 行データの各セルに応じた処理を行う関数の配列
- options: オプション (rowCreator, cellCreator, escapeHtml から複数指定可)

retrieveSigo() ではサーバからの出力 datacellFuncs という名の関数配列で項目を抽出しつつ id="sigotable" のテーブル要素に編集している。この際,rowCreator オブジェクト・オプションを指定し,これに偶数行ごとに背景色を変える関数(= その返値オブジェクト)を対応づけている。また,escapeHtml:false オプションを指定して HTML タグのエスケープをしないようにしている。addRows() 関数は実行のたびに行を追加するだけなので,removeAllRows() 関数で予め行を削除する前処理を行っている。

DWR の JavaScript の書き方,関数仕様詳細は DWR サイト・ドキュメント を参照。悩んだのは cellFuncs 関数配列中の data.sigo 等のオブジェクト指定であった。このうちの sigo の部分は JavaBeans の get〜 メソッドの 〜 を指定しなくちゃならないことに気づくのにえらく時間を要してしまった。それでも MVC デザイン・パターンに従って,しこしこ JSP をコーディングしていたのに比べると,遥かにわかりやすいと思う。DWR なら MVC のうちの C (Controller つまり Servlet) をほぼ意識せずに Web アプリを書くことができるからである。

以上でリソースは整った。Java コードをコンパイルした上で,リソース一式を Tomcat 環境にデプロイしておく。

実行イメージ

misima 漢詩平仄音韻分析』ページに「詩語検索」のボタンを追加した。これをクリックすると,詩語検索のウィンドウがオープンして,詩語の検索ができる。詩語と平仄パターン,韻目が表示される。以下に検索イメージ図を掲げておく。これは平仄パターンが「○-●」(平-仄)もしくは「○-◎」(平-平仄両韻)の 2 字詩語で,かつ「秋」という文字を含む詩語を検索した結果である。ただし,今回作成した版は,直接ユーザが SQL を入力するというイビツにして危険なものであるため,公開は友人限定としている。(2.20 付記:その後,詩語簡易検索機能を一般公開した。その詳細は記事『misimaKansiServlet 漢詩平仄音韻分析・詩語簡易検索』を参照ください。)
 

20110220-expert-search.png

検索イメージ

 

※ 2011.12.23 付記
Spam が多いので限定公開としました。悪しからず。

Old Standard Cyrillic Fonts

ロシアの LaTeX メーリングリスト CyrTeX-ru投稿 #9606 に Old Standard キリル・フォントを LaTeX で使う話題が上がっていた。興味深かったので,私もインストールして使ってみた。

Old Standard フォントは Шрифтовое семейство Old Standard サイトの説明によれば,古典文献学,聖書学,中世史学等の人文科学研究文献に適した多言語フォントとある。OpenType フォントはキリル文字のほか,古スラヴ文字,アクセント付きラテン文字,複式アクセント付きギリシア文字も含んでいる。つまりロシア語旧正書法の文字をも含んでいることになる。もともと Antiqua, New Standard 系のフォントのようである。

Old Standard フォントを LaTeX で使うには二つの方法がある。[1] autoinst による方法と,[2] CyrTeX-ru #9606 リンクにある OldStandard-source.7z を用いた方法である。ただし,いずれの方法においても,ギリシア文字の取扱いは考慮されていない。

[2] では T2A, T1, TS1 フォントエンコーディングが考慮されている。イタリック書体のとき ; (セミコロン) が出力できない問題がある(生成された Type1 フォントのエンコーディング・バグのようである)。これに対し,[1] の方法は T2A, T1, TS1 のほか,古スラヴ文字 T2D エンコーディングが利用可能だし,[2] のセミコロン問題がない。そういう意味で私は方法 [1] を推奨する。導入の手間は前提ツール次第であって,すでにツールが揃っていればどちらの方法もそれほど面倒ではない。[1] は autoinst 関連ツール(『Хартия, Эвристика フォント: Autoinst によるフォント組込み』にメモを残したので,そちらもご覧ください),[1], [2] ともに LCDF ツールを必要とする。

簡単に [1], [2] の UNIX 系システムでの導入方法をメモしておく。ptexlive-2009 環境である。端末操作は tcsh シェルである。私は Mac OS X Tiger, Snow Leopard で実施したが,Linux, FreeBSD でも同じ操作でよいはずである。

autoinst によるインストール

  1. Загрузка шрифтов (フォント・ダウンロード) サイトから oldstandard-2.0.2.otf.zip をダウンロードし,解凍する。
  2. 解凍したディレクトリで autoinst を実行する。--encoding オプションに T2D を指定するためには,t2d.enc エンコーディング・ファイルが予め TeX ツリーに格納されていなければならない。このファイルは PSCyr-0.4-beta フォント・パッケージなどで入手できる(ここにインストール・メモを記した)。
  3. % autoinst --encoding=T2A,T2D,T1,TS1 *.otf

  4. スーパユーザで以下を実行する。autoinst が生成した LaTeX リソースを TeX ツリーに格納しているだけである。これら一連のオペレーションを行うシェルスクリプト autoinst-install.sh を落として使っていただいてもよい。
  5. # setenv TEXDIR /usr/local/texlive/texmf-local
    # mkdir -p $TEXDIR/{fonts/map/dvips,tex/latex}/OldStandard
    # cp -p *.sty *.fd $TEXDIR/tex/latex/OldStandard
    # cp -p *.map $TEXDIR/fonts/map/dvips/OldStandard
    # cd $HOME/.texlive2009/texmf-var
    # find . -name lcdftools -type d | xargs tar cf - | ( cd $TEXDIR; tar xvf - )
    # mktexlsr
    # updmap-sys --enable Map=OldStandard.map

  6. 使い方は,通常のロシア語用 LaTeX 原稿のプリアンブルに \usepackage{OldStandard} を追加すればよい。ロシア語旧正書法テクストを直接入力する場合は,以下のように utf8x inputnec 及び T2D エンコーディングの設定が必要である。処理結果イメージをそのあとに掲げておく。原稿及び PDF もリンクしておく。古風な味わいのあるフォントである。
  7. % -*- coding: utf-8; -*-
    % OldStandard Cyrillic Fonts 
    \documentclass[b5paper]{article}
    \usepackage[T2D,T1]{fontenc}% T2D fontenc
    \usepackage[utf8x]{inputenc}% ucs
    \usepackage[russian]{babel}
    \usepackage{OldStandard}
    \pagestyle{empty}
    \setlength{\textwidth}{100mm}
    % 以下 T2D 用設定
    \def\cyrillicencoding{T2D}% russianb.ldf に T2D 指示
    \makeatletter% \cyrii 互換命令
    \def\@td{T2D}% 
    \let\CYRIItmp=\CYRII% upper case
    \let\cyriitmp=\cyrii% lower case
    \def\cyrii{\ifx\cf@encoding\@td\cyrizhe\else\cyriitmp\fi}%
    \def\CYRII{\ifx\cf@encoding\@td\CYRIZHE\else\CYRIItmp\fi}%
    \makeatother%
    \begin{document}
    Вопросъ о разрывѣ Татьяны съ Онѣгинымъ, несмотря на всю
    ясность. Пушкинскаго стиха, выросъ у насъ въ своего рода 
    \glqq гамлетовскую проблему\grqq,
    къ которой постоянно возвращается критическая мысль.
     
    Онъ, по выраженію Достоевскаго, имѣетъ въ нашей литературѣ
    \glqq своего рода исторію весьма характерную\grqq.
      
    Иначе говоря, вокругъ него сталкивались и боролись 
    различныя враждующія теченія соціально-политической мысли.
     
    И совсѣмъ не случайно выразителями двухъ наиболѣе 
    противоположныхъ взглядовъ на этотъ предметъ являются 
    представители враждующихъ міросозерцаній
    "---соціалистъ-радикалъ Бѣлинскій и 
    націоналистъ-славянофилъ Достоевскій.
     
    \vspace{1em}%
    \small%
    \hfill%
    \parbox[t]{.8\textwidth}{%
    \textit{А. И. Ванновский}~~Зерколо судьбы. (Сон Татьяны.)\\
    в кн.: \textit{С. А. Небольсин}~~Пушкин и европейская традиция. 
    Историко-теоретические работы. М. 1999.}%
     
    \end{document}

    20110210-oldstandard.png


OldStandard-source.7z によるインストール

  1. http://narod.ru/disk/4873172001/OldStandard-source.7z.html から OldStandard-source.7z をダウンロードする。解凍には 7-Zip 圧縮をサポートしたアーカイバが必要である。
  2. Windows MikTeX ユーザなら,添付の fos-FontLab.bat を実行すればよいはずである(私は未確認)。UNIX ユーザは仮想端末から以下を順次実行する。まず,解凍した OldStandard ディレクトリで以下を実行する。これでフォント・リソースが生成される。fos-fontgen.sh としてシェルスクリプトにしたので,これを使ってもよい。
  3. % latex fos-drv.tex
    % latex fos-map.tex
    % foreach i (*.pl)
    foreach? set BS=`basename -s .pl $i`
    foreach? pltotf $BS.pl $BS.tfm
    foreach? end
    % foreach i (*.vpl)
    foreach? set BS=`basename -s .vpl $i`
    foreach? vftovf $BS.vpl $BS.vf $BS.tfm
    foreach? end
    %

  4. スーパユーザでリソースを TeX ツリーにコピーし,マップ登録を実行する。fos-install.sh としてシェルスクリプトにしたので,これを使ってもよい。
  5. # setenv TEXDIR /usr/local/texlive/texmf-local
    # mkdir -p $TEXDIR/{fonts/{tfm,vf,afm,type1,map/dvips},tex/latex}/OldStandard
    # cp -p *.tfm $TEXDIR/fonts/tfm/OldStandard
    # cp -p *.vf $TEXDIR/fonts/vf/OldStandard
    # cp -p *.afm $TEXDIR/fonts/afm/OldStandard
    # cp -p *.pfb $TEXDIR/fonts/type1/OldStandard
    # cp -p fos.map $TEXDIR/fonts/map/dvips/OldStandard
    # cp -p *.fd $TEXDIR/tex/latex/OldStandard
    # mktexlsr
    # updmap-sys --enable Map=fos.map

  6. 以上でインストールは完了。使い方は通常のロシア語 LaTeX 原稿に \renewcommand{\rmdefault}{fos} を追加すればよい。ただしこの場合,T2D エンコーディングは使えない。また,dvipdfmx で PDF を生成する際,イタリックのセミコロンがエラーになるので注意。

Moon Calendar

Profile

ISAO YASUDA。システムエンジニア。神奈川県在住。昭和 30 年代を懐かしむオヤジ。ロシアに興味があります。
[more], [About our site]

Notice

この文書はフィクションであり,実在する個人,団体等とは一切関係ありません。

R-18 指定サイトです。そのうち「18 歳以上ですか」の認証を入れる予定です。

文書の記述内容は無保証です。不適切な表現があればコメントにてご指摘ください。

コメント,トラックバックは,現在,運用を停止しています。ご意見等ありましたら isao@yasuda.homeip.net 宛電子メールにてお願いします。

Links

Entries

About this archive

All Entries of Category 文房清玩

Previous: 夢ノ中ノ日常

Next: 日曜大工

Recent Entries in Main Index.
All Entries in Archive Index.

May 2012

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

文房清玩: Monthly Archives

Powered by Movable Type 5.12 Powered by FreeBSD 8.2-RELEASE
blog counter