以下のような解き方もおもしろいかな。もちろんUTF-8決め打ちならkconv不要。
require'kconv'
"日本語".toutf8.unpack('U*').sizeArray#injectは凄い便利なので、artonさんのString#char_countも以下のように書き換えられます。
class String
def char_count
cnt = 0
self.scan(/./).inject(0){|r,i|r+1}
cnt
end
endもっと力技で数える方法もあります。つまり、バイト列を1バイトずつ見ていって、数えていく方法。毎度毎度書くのは大変ですが、メソッドを定義してしまうなら速い方がいいですもんね。
class String
def char_count_jis
count = 0
i = 0
while i < self.length
if self[i] == 0x1B
i += 1
case self[i]
when ?$
i += 1
case self[i]
when ?@,?B
i += 1
while self[i] && self[i] > 0x20
i += 1
if self[i] > 0x20
i += 1
count += 1
else
throw 'invalid argument %c/%X' % [self[i],self[i]]
end
end
when ?(
i += 1
case self[i]
when ?D,?O,?P,?Q,??
i += 1
while self[i] && self[i] > 0x20
i += 1
if self[i] > 0x20
i += 1
count += 1
else
throw 'invalid argument %c/%X' % [self[i],self[i]]
end
end
end
end
when ?(
i += 1
case self[i]
when ?I,?B,?J,?H
i += 1
while self[i] && self[i] > 0x20
i += 1
count += 1
end
end
end
else
throw 'invalid argument %c/%X' % [self[i],self[i]]
end
end
count
end
def char_count_sjis
count = 0
trail = false
self.each_byte do |c|
if trail
trail = false
elsif c < 0x80 || (c-32)>>6 == 2
count += 1
else
trail = true
count += 1
end
end
count
end
def char_count_euc
count = 0
self.each_byte{|c|count += c==0x8F ? 2 : c < 0x80 ? 6 : 3}
count / 6
end
def char_count_utf8
count = 0
self.each_byte{|c|c>>6==2||count+=1}
count
end
def char_count_utf16
count = 0
self.each_byte{count+=1}
count / 2
end
def char_count_utf32
count = 0
self.each_byte{count+=1}
count / 4
end
end最初の JIS は完璧に力技です。エスケープシーケンスを見て行き、それに対応したバイトごとに文字数を加算していきます。拡張したい方(いるのか?)は ISO-IR を見ながらコードを追加すればISO 2022 系なら何でも対応できます。
次の Shift_JIS になるとぐっと短くなりました。状態を持たないっていいですねぇ。いかに開発当時 Shift_JIS が画期的だったかこれだけでも推察できそうな気がしないでもないです(何 ちなみに、半角カタカナにももちろん対応しています。
EUC-JP ではとうとう実質一行にできました。EUC-JP は Shift_JIS のように トレールバイトが 0x80 以下に入ってくることが無いのでだいぶ楽に書くことができます。途中の count が実際の文字数の 6 倍になっているのは少々トリッキーかもしれません。一応説明すると、EUC-JP は 1 octet, 2 octet, 3 octet から構成されるので、1, 2, 3 の公倍数をとっているわけですね。
UTF-8 ではさらに短くなっています。これは UTF-8 ではリードバイトとトレールバイトが別の空間に存在しているためです。つまり、UTF-8 はトレールバイトが常に 0x80 - 0xBF に位置しているため、それ以外のときだけ数えているわけです。
UTF-16 と UTF-32 もついでに書いておきました。これは言うまでもありませんね。