Search Non ASCII with bytes
ASCII Compatible な encoding の文字列に、ASCII 文字以外が含まれているかどうか検索するという問題。Ruby においては ASCII のみの文字列は ASCII 互換 encoding の文字列と結合できるので重要な問題です。
普通に書くと以下のようになるでしょうか。しかし、これでは 1000 文字を 100 万回調べるのに 160 秒ほどかかってしまいました。
int search_nonascii_bytes(const char* p, const char *e) { for (; p < e; ++p) { if (!isascii(*p)) return p; } return NULL; }
String Optimization
最近の趣味は文字列処理の高速化なんですが、それについてメモがてら。
Shift_JIS 風 UTF-8 と CP932 風 UTF-8 の正規化
str.gsub!(/\xC2\xA2/, "\xEF\xBF\xA0") str.gsub!(/\xC2\xA3/, "\xEF\xBF\xA1") str.gsub!(/\xC2\xAC/, "\xEF\xBF\xA2") str.gsub!(/\xC2\xA6/, "\xEF\xBF\xA4") str.gsub!(/\xE2\x80\x94/, "\xE2\x80\x95") str.gsub!(/\xE2\x80\x96/, "\xE2\x88\xA5") str.gsub!(/\xE3\x80\x9C/, "\xEF\xBD\x9E") str.gsub!(/\xE2\x88\x92/, "\xEF\xBC\x8D")
で正規化できます。
Ruby1.9 なら以下のように書いた方がベター。
str.tr("\u{301C 2016 2212 00A2 00A3 00AC 2014 00A6}", "\u{FF5E 2225 FF0D FFE0 FFE1 FFE2 2015 FFE4}")
Ruby 1.9 の magic comment
magic comment ですが、端的に言えば Python と同じです。異なるのは、emacs 風の -*- coding: euc-jp-unix -*- の -unix をちゃんと捨ててくれる点くらいでしょうか。これもあくまで現在の実装になります。
Ruby 1.9 における replica と alias の違い
端的にいえば以下の通り。
- replica
- 「encoding」を定義する
- alias
- 「encoding *名*」を定義する
図にすると以下のような感じ。
鬼車(CES) OnigEncodingSJIS │←────────────────┐ ───┼─────────────────┼────────────── │ │ Encoding (文字集合+CES) │ │ #<Encoding:Shift_JIS> ─(replica)→ #<Encoding:Windows-31J> │ │ │ │ ───┼─────┼───────────┼─────┼──────── │ (alias) │ (alias) Encoding名 │ ↓ │ ↓ (charset名) "Shift_JIS" "SJIS" "Windows-31J" "CP932"
(鬼車層とEncoding層の区別は正確ではないのですが便宜上こうしておきます。実際には Encoding 層 での #
鬼車層では文字単位の操作を行っています。具体的には str[n] が何を返すか、str.length はどのくらいか。また String#valid_encoding? の true/false 判定もこのレベルです。鬼車層の実体は、文字の最小バイト数と最大バイト数の指定や、文字列の長さを算出する関数、大文字を小文字に変換する関数等をひとまとめにした構造体 OnigEncodingType で、具体的には ONIG_ENCODING_ASCII やONIG_ENCODING_SJIS 等があります。
Encoding 層では文字列操作全般を司ります。具体的には文字列の結合可能・比較可能、正規表現のマッチ可能判定がこの層で行われています。この層での実体は rb_encoding です。Encoding 層における実体は 鬼車が native に対応している encoding から直結している original encoding、original encoding からreplicate された replica encoding、鬼車が対応していない encoding に便宜的に与えられる dummy encoding の3種類に分けることができます。これらを見分けるには Encoding#base_encoding や Encoding#dummy? を見るわけですが、見たからどうということでもありません。
これらの区別を超越する存在が 7bit のみで構成された文字列で、String#ascii_only? で判定可能です。これが真になる文字列はいかなるencoding の文字列とも結合・比較可能となります。
なお、rb_encoding は OnigEncodingType の同じもののため、Encoding 層でのreplica でも、鬼車レベルの諸関数、例えば文字群に Unicode Property Nameを与えたり、valid なバイト列の範囲を定義し直すことが可能です。([ruby-dev:32947] での「レプリカはコピーをした後で関数のいくつかを上書きすることも可能」とはたぶんこのこと)
Encoding名層は、Encoding.find() するためのテーブルです。実際の構造は以下のようになっていると考えればいいでしょう。alias で追加された名前は、文字列から rb_encoding を検索する際にのみに用いられます。
ENC_TABLE = { 'Shift_JIS' => Encoding::Shift_JIS, 'Windows-31J' => Encoding::Windows_31J, 'EUC-JP' => Encoding::EUC_JP } ENC_TABLE_ALIAS = { 'SJIS' => Encoding::Shift_JIS, 'CP932' => Encoding::Windows_31J, 'EUCJP' => Encoding::EUC_JP } def rb_enc_alias(alias, orig) ENC_TABLE_ALIAS[alias] = orig end def Encoding.find(name) return ENC_TABLE[name] if ENC_TABLE[encname] return ENC_TABLE_ALIAS[name] if ENC_TABLE_ALIAS[encname] return -1 end
RubyM17N について
RubyM17N は現在「コードが仕様」な面が多いため、わたしが知っていることについて書き残しておくことにする。と言っても、わたしも追いかける身なので間違ってる点はあるだろうので、そこはご了承を。