Oct 29, 2004

dirifyについて考えてみた。

Movable Typeの「dirify」というのは、DIR-ify、すなわち文字列をファイルシステムに適した文字列に変換する操作を提供しています。MT tagに対するグローバルフィルターとして提供されているだけではなく、Movable Typeの内部でさまざまな局面(アーカイブファイルの名前の決定など)で利用されています。

その機能は大まかに言って以下の手続きで実現されています。

  1. [DefaultLanguageがja以外のとき] まず与えられた文字列のHigh ASCII文字(=最上位ビットが有効な文字群)のうち可能なものは7bit ASCII文字に置換する。例えば、ÀÁÂÃÅなどをAに置換する。
  2. 文字列を小文字に変換する。
  3. 文字列からHTMLタグを除去する。
  4. 文字列からHTML Entities (&XXX;というパターン)を除去する。
  5. 空白文字、アルファベット、数字以外を除去する。
  6. 空白文字をアンダースコアに置換する。

この仕様はLatin-1言語圏の人々の一定の支持を勝ち得ているようです。たとえMovable Type Plugin Directory: DirifyPlusのような亜種があるにしても。

しかし、今あえてはっきり言いたいことがあります。それは、「この仕様はクズである」ということです。

そもそも、空白文字は「_」に変換されますが、「!"#$%&'()-=^~\|@`[{;+:*]},<.>/?」はすべてヌル文字に変換されます。つまりこれらは空白文字より区切り文字としての順位が低いということになります。果たしてそうでしょうか? 「:-/,.」は文中、語中で明確に区切りを表す記号ではないでしょうか?

文字実体参照、数値文字参照はこれらの文字より不幸です。&nbsp;や&#160;はやはりヌル文字に変換されますが、意味的に空白文字より弱いのでしょうか。

そうそう忘れていました、Latin-1語圏(西欧諸国)などで普通にUTF-8をPublishCharsetに使っている人々にも不幸は平等に訪れます。High ASCII文字はUTF-8では2バイトに拡張されて表現されますから、dirifyに組み込まれているiso-8859-1用の置換ルールは適用されません。ちなみにこのあたりをうまく扱うMT Stuff: Dirify for Unicodeというプラグインもあります。が、これで何とかなったと思ったそこのフランス人は相当お人よしで、MTの内部では相変わらずオリジナルのdirifyが使われているわけなんですYO!

また、Latin-2,3,...語圏でiso-8859-2,3などをPublishCharsetに使っている場合にもdirifyはiso-8859-1用の置換ルールを適用します(それしかないのですから)。これは望ましい結果をもたらすかもしれないし、そうでないかもしれませんね。あいにくとチェコ語を入力できるキーボードは持っていないので確認できませんが…。

…まったく何も考えられていませんね。

しかし、それより何より気に食わないことがあるのです。それは日本語を含むMultibyte文字がアルファベットに変換されないのはいいとして、ヌル文字列に変換されるために区切りとしてすら機能しないことです。例えば「Movable Type使っててDBを壊したのYO!」の変換結果は「movable_typedbyo」となります。おかしくないですか? 「movable_type_db_yo」の方がまだDBに関する話であることが分かりやすいでしょう。所詮全角スペース「 」はヌル文字に置換され、半角スペースには勝てない存在なのです。

こうして考えてみると、たかがdirifyと言えども奥が深いですよね。このエントリでいずれ日本語ユーザ向けにもう少しましなdirifyを作って公開したいと思いますが、時間がないのでもう少し先になりそうです。

2005-06-13追記:
私が普段使っているdirifyアルゴリズムを公開しておきます。MT3.17以降用のdirifyです。HTML Entitiesを除去するまではMTオリジナルのものと共通で、その後以下の変換を行っています。

  • 数字・アルファベットを除く文字を「_」に変換する。
  • 先頭および末尾の「_」を除去する。
  • 連続する「_」を一個の「_」に変換する。
sub iso_dirify {
    my $s = $_[0];
    require MT;
    if (MT::ConfigMgr->instance->DefaultLanguage eq 'ja') {
        $s = MT::I18N::encode_text($s, undef, 'euc-jp');
    } else {
        $s = convert_high_ascii($s);  ## convert high-ASCII chars to 7bit.
    }
 
    $s = lc $s;                   ## lower-case.
    $s = remove_html($s);         ## remove HTML tags.
    $s =~ s!&[^;\s]+;!!g;         ## remove HTML entities.
    $s =~ s![^\w\s]!!g;           ## remove non-word/space chars.
    $s =~ tr! !_!s;               ## change space chars to underscores.
    $s =~ s![^\w]!_!g;
    $s =~ s/(^_+|_+$)//g;
    $s =~ s!_+!_!g;
    $s;
}
 
sub utf8_dirify {
    my $s = $_[0];
    $s = xliterate_utf8($s);      ## convert two-byte UTF-8 chars to 7bit ASCII
    $s = lc $s;                   ## lower-case.
    $s = remove_html($s);         ## remove HTML tags.
    $s =~ s!&[^;\s]+;!!g;         ## remove HTML entities.
    $s =~ s![^\w\s-]!!g;           ## remove non-word/space chars.
    $s =~ tr! !_!s;               ## change space chars to underscores.
    $s =~ s![^\w]!_!g;
    $s =~ s/(^_+|_+$)//g;
    $s =~ s!_+!_!g;
    $s;
}

About Me

My Photo

つくばで働く研究者LV15

Total Pageviews

Amazon

Copyright 2012 Ogawa::Buzz | Powered by Blogger
Design by Web2feel | Blogger Template by NewBloggerThemes.com