nkc+
先日、『初めて文字コードで』に書いた文字コード変換で、どうしても QKC っぽい感じで変換をしたいので、ちょっと考えてから、もう公開されていない nkc のコードを読んでいた。
ruby という言語は、実は今まで一回も触ったことがないのだけど、まあ見て何も分からないというわけでもない。見ると、日本語コードの判定のために専用のライブラリを書いているのだが……そうか、これって ruby1.8 時代のコードなんだよな、と気付いた。ruby はかつて Perl よりも日本語処理においてパワフルでなかった時期があって、その頃に書かれたこのコードは、検出し誤りを防ぐために、検出部を自力で書いている。そして変換は iconv で行っている……のだが、今は、ruby のモジュールで新しい nkf の機能がフルに使えるので、こんなことをしなくてもいいかもしれない。
現在の nkf というのは、実は結構強力で、--guess オプションを使うと簡単に文字列のエンコーディングを判定できる。これを使わない手はない……ということで、独自ライブラリと iconv を使うところを nkf で済ませるように書き換える。まあね、ruby いじるの初めてでも、これ位はいけるんですよ、ええ……
などとやっていたのだが、1箇所で思いっきりハマった。改行コードの変換に gsub を使って、
body = body.gsub(/(\r\n|\r|\n)/) { newline }……などと書いていたのだけど、場合によってここでエラーが出てしまう。んー???と思いつつ挙動を探ると、どうも body に ISO-2022-JP の文字列が入っているとこうなる、らしい。うーん???……などと考えつつ、30分程を空費してしまう。
おそらく ruby に慣れた方は、ここまで読んで「はいはい」と気付かれたことだろう。僕は、ruby が ISO-2022-JP の文字列操作を直接できないことを、今日まで知らなかったのだ。そりゃそうだ。ruby のスクリプト書くのは今日が初めてなんだから。しかしなあ。
え。で、どうしたか、って?
……まあこれで、QKC に UTF-8 拡張をしたのと同等の処理ができるようにはなった。なったけれど、これは、美しくない。ダメダメだ。あ゛ー。if to == "ISO-2022-JP" then
body = NKF.nkf("-m0 --cp932 --oc=UTF-8", body)
body = body.gsub(/(\r\n|\r|\n)/) { newline }
body = NKF.nkf("-m0 --cp932 --oc=ISO-2022-JP", body)
else
body = body.gsub(/(\r\n|\r|\n)/) { newline }
end
【後記】後で気付いて、gsub を使わず NKF で変換するように変更。
……こんな感じ。こういう発想が出て来ないという時点でダメダメだ。頭が固くなってる。if newline then
case newline
when "\r\n"
print " (CR+LF)"
optio = "-m0 -x -Lw -c --ic="+to+" --oc="+to
when "\r"
print " (CR)"
optio = "-m0 -x -Lm --ic="+to+" --oc="+to
when "\n"
print " (LF)"
optio = "-m0 -x -Lu -d --ic="+to+" --oc="+to
end
body = NKF.nkf(optio, body)
end
……などとやって、夕食後、手持ちのテキストで変換を試していて、再びドツボにはまる。文字コード検出で不具合が出てしまうのだ。
のようにしてコード種別の検出を行っていたのだけど、CODES = {
NKF::JIS => "ISO-2022-JP",
NKF::EUC => "EUC-JP",
NKF::SJIS => "Shift_JIS",
NKF::UTF8 => "UTF-8",
NKF::ASCII => "ASCII",
NKF::BINARY => "BINARY",
NKF::UNKNOWN => "UNKNOWN",
}
(中略)
from = CODES.fetch NKF.guess(body)
と怒られる。うーん……と、NKF の仕様を読んだり、ネットの海を彷徨ったりした挙句、/usr/local/bin/nkc:96:in `fetch': key not found: #<Encoding:Windows-31J> (KeyError)
とする。--CP932 オプションも、不要な部分は消してしまう。これで……今度は OK ね。はぁ。CODES = {
NKF::JIS => "ISO-2022-JP",
NKF::EUC => "EUC-JP",
NKF::SJIS => "Shift_JIS",
NKF::UTF8 => "UTF-8",
NKF::ASCII => "ASCII",
NKF::BINARY => "BINARY",
NKF::UNKNOWN => "UNKNOWN",
Encoding::EUCJP_MS => "EUC-JP",
Encoding::CP51932 => "EUC-JP",
Encoding::WINDOWS_31J => "Shift_JIS",
}
(中略)
from = CODES.fetch NKF.guess(body)
うーむ。やはり perl で書き換えた方が早かったような気がする。僕は ruby のエヴァンジェリストでも何でもないしなあ。
【後記】後でちょっと考えて、半角カナの処理も付け加える。まあ、この辺にしておこう。いわゆる「外字」のことまでケアするとなると面倒な話になりそうだしなあ……はあ。