UTF-8への対応

作成日:2007-11-25
最終更新日:

utf-8の文書をNamazuで扱うには

utf-8で記述されたページに、フォームで Namazu の検索欄を設けたとする。 ここから検索を行うと、文字化けが生じる。 これを解決するための一つの方法は、ラッパープログラム、 即ちutf-8の文字コードをeucに変換することである。 namazuでutf8ファイルの検索 (www.keknet.net) というページはこれを行っている。これを元にして、 自分でプログラムを作ってみた。次のサンプルである。

ラッパーのサンプル

#!/usr/bin/perl

use CGI;
use Text::Iconv;
use CGI::Lite;

my $converter = Text::Iconv->new("UTF8", "EUC-JP");
my $cgi = new CGI;
my $new_query = $converter->convert($cgi->param('query'));
$uencode = url_encode($new_query);
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?query="."$uencode\n\n";

改良すべき点

ところが上記の版では動かないことがわかった。 以下原因と対策を述べる。

Text::Iconv がインストールされていない

私が利用しているプロバイダでは、 Text::Iconv がインストールされていなかった。 止むを得ず、自分でインストールした。

Iconv で変換がされない

Iconv で変換前と変換後の文字コード種類を指定するが、 文字コード名にばらつきがあるようだ。 私が参考にしたのは、Ruby レシピブックのp.39で、 ここにある文字列で文字コード種類を指定したところ、変換された。 大文字小文字の区別も重要。

検索条件が伝わらない

検索においては、検索するキーワード(検索式)だけでなく、 CGI 変数も指定しなければならない。 CGI 変数の例は、Namazu のマニュアルの、 form の設定 という個所に記載されている。これを引き渡す必要がある。

最初は、たとえば、表示言語を引き渡すために、 次のようなことを考えていた。
$ulang = url_encode($cgi->param('lang'));
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?query="."$query&lang="."$ulang\n\n";
しかしこの方法では、表示言語以外の CGI 変数に対して同様なことを繰り返す必要があり、面倒である。 今回は、CGI 変数 query のみに対して utf-8 への変換を行えばよい。 そこで、cgi 変数の query クラス変数のみを更新して、 メソッド query_string を呼び出せばよいと考えた。 これならば、どの CGI 変数が定義されているかを考える必要はない。 以上を合わせて、次が改良版となる。


ラッパーの改良版


#!/usr/bin/perl -I/$HOME/...

use CGI;
use Text::Iconv;
use CGI::Lite;

my $converter = Text::Iconv->new("UTF-8", "eucJP");  
my $cgi = new CGI;
my $new_query = $converter->convert($cgi->param('query'));
$cgi->param('query', $new_query);
$query_str = $cgi->query_string;
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?"."$query_str\n\n";

文字化け対策

これは必ずしもUTF-8とは関係ないが、文字化け対策として乗せておく。 あるとき、次のように環境設定して cron で起動させたが、タイトルと要約文が文字化けする。 それどころか、キーワードでの検索にも該当文書が表示されない。

LANGUAGE="ja_JP.EUC";export LANGUAGE
$HOME/local/bin/mknmz -O $HOME/local/var/namazu/index $HOME/internet/www.ne.jp/asahi/music/marinkyo 

何だかわからないが、環境変数を沢山設定した。

LC_ALL=ja_JP.EUC; export LC_ALL
LANG=ja_JP.EUC; export LANG
LANGUAGE="ja_JP.EUC";export LANGUAGE

こうしたところ、タイトルと要約文の文字化けはなくなった。もちろん、該当文書も表示される。 (2009-11-09)

Ruby によるラッパー

今まで使っていたホスティングのサーバとは別に、 新たなサーバが使えることになった。こちらについても Namazu を乗せた。 ただし、比較のために分かち書きに Kakasi でなく Mecab を用いた版である。 こちらのラッパープログラムも必要なので、今度は Ruby で書いてみた。 簡単だと思いきや、思いのほか苦労した。

#!/usr/local/bin/ruby

require 'cgi'
require 'iconv'

cgi = CGI.new
new_query = CGI.escape(Iconv.conv("eucJP", "UTF-8", cgi.params['query']))
cgi.params['query'] = new_query
query_str = cgi.query_string
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?"+query_str+"\n\n";

稚拙なプログラムではあるが、動けばいいだろうと思い書いたプログラムであったが、動かなかった。 問題はcgi.params['query'] = new_query の部分であり、cgi インスタンスは読み取りしかできず、 更新はできないのである。 実は上のプログラムは別の意味でも間違っていて、cgi.params['query'] は配列であり、文字列ではない。 だから上記の cgi.params['query'] は cgi.param['query'][0] と変更すべきだが、 更新ができないことには変わりない。

その後試行錯誤を経て ここでは言えないぐらい恥ずかしいことを行い、たどり着いたのが次のプログラムである。

#!/usr/local/bin/ruby

require 'cgi'
require 'iconv'

cgi = CGI.new
query_str = ""

cgi.params.each{|key, value|
  query_str += key + "=" + CGI.escape(Iconv.conv("eucJP", "UTF-8", value[0])) + ";"
}
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?"+query_str+"\n\n";

それぞれのハッシュに対して UTF-8 を一度 EUC に変換した後、escape して問題ないようにしている。 ( 2009-10-28 )

続:Ruby によるラッパー

慧眼な読者は、上記のプログラムでも誤っていることがわかるだろう。 その理由は、cgi.params というハッシュの key が配列であるにもかかわらず、 その配列の要素が一個だけという前提に立ったものであるからだ。 CGI では、同じ key に対して複数の value があることを許容する。 従って、Ruby の実装では、cgi.params の value を配列として、 複数の value に対応している。 実は、私も上のプログラムに以上の誤りがあることは承知で載せた。 それは、value が配列であることに対応して作成したプログラムが、 なぜかエラーで異常終了するからだった。そのときは理由がわからず、 とにかく公開しようと焦ったのだった。

その後冷静な目で異常終了するプログラムを見てみると、原因がわかった。 あるべき個所にカッコがなかったのだ。ああ、恥ずかしい。 カッコを付けたら正常に動作した。正しいプログラムを示す。

#!/usr/local/bin/ruby

require 'cgi'
require 'iconv'

cgi = CGI.new
query_str = ""

cgi.params.each{|key, values|
  values.each{|a_value|
    query_str += key + "=" + CGI.escape(Iconv.conv("eucJP", "UTF-8", a_value)) + ";"
	}
}
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?"+query_str+"\n\n";

配列であることを明確にするため、cgi.params から取り出す要素を values と変更し、 values から更に要素をとる a_value という変数を作った。( 2009-12-06 )

iconv ライブラリ

その後しばらくして、また動かなくなってしまった。 CGI のエラーログには次のようにある。

[xxx xxx xx 03:10:38.940477 2017] [cgi:error] [pid 98349] [client xxx.xxx.xxx.xxx:xxxxx] AHxxxxx: \
	  /xxx/xxx/kernel_require.rb:55:in `require': cannot load such file -- iconv (LoadError): \
	  /xxx/xxx/xxx/xxx/xxx/xxx/namazu.cgi, referer: http://xxx.jp/xxx/xxx.html

[xxx xxx xx 03:10:38.940665 2017] [cgi:error] [pid 98349] [client xxx.xxx.xxx.xxx:xxxxx] AHxxxxx: \
	\tfrom /xxx/xxx/kernel_require.rb:55:in `require': \
	  /xxx/xxx/xxx/xxx/xxx/xxx/namazu.cgi,

[xxx xxx xx 03:10:38.940734 2017] [cgi:error] [pid 98349] [client xxx.xxx.xxx.xxx:xxxxx] AHxxxxx: \
	\tfrom namazu.cgi:4:in `<main>': \
	  /xxx/xxx/xxx/xxx/xxx/xxx/namazu.cgi,	referer: http://xxx.jp/xxx/xxx.html

[xxx xxx xx 03:10:38.942220 2017] [cgi:error] [pid 98349] [client xxx.xxx.xxx.xxx:xxxxx] \
	End of script output before headers: namazu.cgi, referer: http://xxx.jp/xxx/xxx.html

iconv というライブラリはないよ、といっている。今まで気が付かなかったのがおまぬけなのだが、 さてどうしよう。iconv は Ruby 2.0.0 dev の時点から利用できなくなっていることを知らなかった。 下記のページに告知がある。

http://magazine.rubyist.net/?0041-200Special-note

今後は String#encode や Encoding::Converter、あるいはどうしても iconv が必要な場合は iconv.gem を用いてください

このようにあるので、String#encode を使うのがよいのだろう。 機械的な書き換えはできるのだろうか。

#!/usr/local/bin/ruby -w

require 'cgi'

cgi = CGI.new
query_str = ""

cgi.params.each{|key, values|
  values.each{|a_value|
    query_str += key + "=" + CGI.escape(a_value.encode("eucJP", "UTF-8")) + ";"
	}
}
print "Location: http://marinkyo.que.jp/cgi-bin/sercxo/namazu2.cgi?"+query_str+"\n\n";

案ずるより生むがやすし。Iconv.conv("eucJP", "UTF-8", a_value) を a_value.encode("eucJP", "UTF-8") に変えればよかった ( 2017-05-04 ) 。

タイトルの文字化け

以上の処置を施しても、字上符(ダイアクリティカルマーク)のついた文字は、 検索の対象にできない。 せめて、タイトルや要約文の表示は正しく表示されてほしいのだが、 いくつかの文字は消えたり読めなくなったりしている。 たとえばエスペラントの字ĜĤĴŜŬ ĉĝĥĵŝŭがある。 スカルラッティの部屋というページのエスペラント版を Ĉambro de Scarlatti という名前の表題で作っている (HTML 上は <title> と </title> ではさまれた文字列である)。 かりに、Scarlatti というキーワードで検索した時、 候補としてこのページが提示されたとする。このときのタイトルは、 ambro de Scarlatti となってしまっていて、先頭の Ĉ の文字が消えてしまっている。

これは Namazu の問題か ntf の問題かはわからない (おそらく前者とみている)。 この脱落を防ぐには、 Namazu のインデックスフォルダの namazu.field.subject を直接変更して、 そのあとで rfnmz indexdirectoryを行うしかない。 そしてこれらの書き換えは、Namazu を通さず別のプログラムで行うべきかもしれない。

まりんきょ学問所全文検索システム Namazu > UTF-8への対応


MARUYAMA Satosi