[ top ] [ prev ] [ up ] [ next ]
半角カナと絵文字
Rubyにおける半角カナに言及されることはほとんどありません。
Rubyと半角カナとの関係はとても希薄なのです。
Rubyの中で半角カナに言及されているのは
ext/nkf/nkf1.7/nkf.c:
** x Do not convert X0201 kana into X0208
くらいでしょう。
しかしながら、RubyでCGI、それも携帯電話で表示するためのCGIを作るとなると、現状では半角カナを扱わざるを得ません。
そこでRubyで半角カナとお付き合いする方法を考えます。
ついでに文字変換ネタとして携帯電話(i-mode, JSKY, ezWeb)の絵文字も扱います。
半角カナ
Rubyで半角カナを全角カナにするにはnkfを用いて
require 'nkf'
NKF::nkf('-Ss', str)
とします(strはSJISの文字列)。
Rubyで全角カナを半角カナにするには? 変換するメソッドを書けばいいでしょう。
絵文字
- 各キャリアの絵文字
-
http://www.nttdocomo.co.jp/mc-user/i/tag/emoji/
http://www.dp.j-phone.com/dp/tool_dl/web/picword_top.php
http://www.au.kddi.com/ezfactory/tec/spec/3.html
- 各キャリアの絵文字を使ったメールを変換するサービス
-
http://emojibin.jp/index.html
- i-mode絵文字をパソコンで表示・入力するための外字と辞書
-
http://www.vector.co.jp/soft/win95/writing/se124240.html
i-modeの絵文字
とりあえずi-modeの絵文字で入力することにしましょう。
もしもデータの中に絵文字コードを置きたくないのなら、
def emoji(str)
str.gsub(/\[([A-Za-z0-9]{4})\]/) {
sjis16 = $1
code = [sjis16].pack('H*')
code
}
end
このようなメソッドを用意すればいいでしょう。この例ではSJIS16進での入力を想定しています。たとえば [F9F4]
で「ひよこ」になります。
JSKYの絵文字
J-PHONEはそろそろボーダフォンに染まってくるようですが、JSKYの絵文字の互換性は確保されるようです。
ネットファーム・コミュニケーションズ株式会社 の携帯ホームページ作成ソフト iど〜も!V3 マルチキャリアエディション は、i-modeだけでなくJSKY, ezWeb用のページも作れますし、絵文字も扱えます。
ここでは iど〜も! の変換機能を用いて絵文字の変換表(i-mode=>JSKY)を作ります。
ezWebの絵文字
これも同じく iど〜も! の変換機能を用いて変換表(i-mode=>ezWeb)を作ります。
USER_AGENT
CGIで扱えるように ENV['USER_AGENT'] を見て絵文字を変換することにします。
仕様
#!/usr/local/bin/ruby
def zen2han(string, user_agent=nil)
#...
end
if __FILE__ == $0
puts zen2han("アあアあア")
puts zen2han("ヴ")
str = 0xF8.chr<<0x9F.chr # imode:晴れ
puts zen2han(str)
puts zen2han(str, 'DoCoMo')
puts zen2han(str, 'J-PHONE')
puts zen2han(str, 'UP.Browser')
end
こんな感じ。
変換表
カナ変換も絵文字変換も単に文字列に含まれる文字コードを変換するだけですので、変換表さえ作れば一件落着。
効率を考えて、gsubを使わずにeach_byteで1バイトづつ見て処理することにします。
全角=>半角(別にカナでなくても扱えるし)の変換表をZ2H0, Z2H1というHashに格納。Z2H0は全角の1バイト目、Z2H1は全角の2バイト目に対応します(原始データはSJISで決めうち)。
絵文字の変換表をEmoji_vfone, Emoji_ezwebというHashに格納。
#!/usr/local/bin/ruby
require 'z2h'
def zen2han(string, user_agent=nil)
ret = ''
hi = nil
emoji = nil
if user_agent
case user_agent
# when /DoCoMo/n
# require 'emoji_imode'
# emoji = Emoji_imode
when /J-PHONE/n
require 'emoji_vfone'
emoji = Emoji_vfone
when /UP\.Browser/n
require 'emoji_ezweb'
emoji = Emoji_ezweb
end
end
string.each_byte do |c|
if hi
lo = c
x0 = Z2H0[hi] && Z2H0[hi][lo]
if x0
ret << x0.chr
x1 = Z2H1[hi][lo]
if x1
ret << x1.chr
end
else
if emoji && emoji[hi] && (em = emoji[hi][lo])
ret << em
else
ret << hi.chr << lo.chr
end
end
hi = nil
else
case c
# when 0x81..0x83
when 0x80..0xFF
hi = c
else
ret << c.chr
end
end
end
ret
end
変換表の作成
全角=>半角
とりあえず、変換しそうな範囲のキャラクタを一覧表にしてみます。
Ctrl-D, Ctrl-ZはRubyのgetsを止めてしまうのでエスケープしておきます。
#!/usr/local/bin/ruby
(0x81..0x83).each do |hi|
(0x00..0xFF).each do |lo|
case lo
when ?\C-D
print "[CTRL-D]"
next
when ?\C-Z
print "[CTRL-Z]"
next
end
print hi.chr, lo.chr, "\t"
end
end
んで、この結果を z.txt, h.txt に保存します。
z.txt はそのまま置いておき、h.txt を(秀丸とかで)半角に変換します。
用意できたら、後はこれを元にHashを作ります。例えば
#!/usr/local/bin/ruby
z_tab = IO.read("z.txt").split(/\t/)
h_tab = IO.read("h.txt").split(/\t/)
p z_tab.size
p h_tab.size
z2h0 = {}
z2h1 = {}
(0x81..0x83).each do |hi|
z2h0[hi] = open("z2h0_%02X.rb" % hi, "w")
z2h0[hi].print <<EOT
Z2H0[0x#{"%02X"%hi}] = {
EOT
z2h1[hi] = open("z2h1_%02X.rb" % hi, "w")
z2h1[hi].print <<EOT
Z2H1[0x#{"%02X"%hi}] = {
EOT
end
z_tab.size.times do |i|
z = z_tab[i]
h = h_tab[i]
if z != h
next if z[0] == h[0] && z[1] == 0x00 && h[1] == 0x20
next if z[0] == h[0] && z[1] == 0x0D && h[1] == 0x0A
# puts "%02X %02X\t%02X %02X\t%d" % [z[0],z[1],h[0],h[1],z[1]-h[0]]
z2h0[z[0]].puts "\t0x%02X => 0x%02X, " % [z[1],h[0]]
unless h[1].nil?
z2h1[z[0]].puts "\t0x%02X => 0x%02X, " % [z[1],h[1]]
end
end
end
(0x81..0x83).each do |hi|
z2h0[hi].print <<EOT
}
EOT
z2h0[hi].close
z2h1[hi].print <<EOT
}
EOT
z2h1[hi].close
end
f = open("z2h.rb", "w")
f.print <<EOT
Z2H0 = {}
Z2H1 = {}
EOT
(0x81..0x83).each do |hi|
src = open("z2h0_%02X.rb" % hi, "r")
while line = src.gets
f.print line
end
src.close
src = open("z2h1_%02X.rb" % hi, "r")
while line = src.gets
f.print line
end
src.close
end
f.close
このように(すげー手抜きしてまんなあ)。z2h.rb が完成。
絵文字
絵文字の変換表も 元になるデータ が用意できれば
def emoji_list(car)
list = []
open("#{car}/index.htm") do |f|
while line = f.gets
if /(.*)<br>$/n =~ line
list.push $1
elsif /<img localsrc="(.*)" \/>/n =~ line
list.push [nil, $1.to_i]
elsif /(.*)<br \/>$/n =~ line
list.push $1
end
end
end
list
end
emoji_table = {}
%w/ i j ez /.each do |car|
emoji_table[car] = emoji_list(car)
end
cars = %w/ vfone ezweb /
f_ = {}
cars.each do |car|
f_[car] = {}
(0xF8..0xF9).each do |hi|
f_[car][hi] = open("emoji_#{car}_#{"%02X"%hi}.rb",'w')
f_[car][hi].print <<EOT
Emoji_#{car}[#{"0x%02X"%hi}] = {
EOT
end
end
emoji_table['i'].each_with_index do |imode, index|
vfone = emoji_table['j'][index]
unless vfone.empty?
f = f_["vfone"][imode[0]]
f.print "\t"
f.print %(0x#{"%02X"%imode[1]} => )
ary = []
vfone.each_byte{|c| ary.push "0x%02X.chr"%c }
f.print ary.join(' << ')
f.print ', '
f.print "\n"
end
ezweb = emoji_table['ez'][index]
unless ezweb.empty?
f = f_["ezweb"][imode[0]]
f.print "\t"
f.print %(0x#{"%02X"%imode[1]} => )
if ezweb[0].nil?
f.print %('<img localsrc="#{ezweb[1]}" />', )
else
ary = []
ezweb.each_byte{|c| ary.push "0x%02X.chr"%c }
f.print ary.join(' << ')
f.print ', '
end
f.print "\n"
end
end
cars.each do |car|
(0xF8..0xF9).each do |hi|
f_[car][hi].print <<EOT
}
EOT
f_[car][hi].close
end
end
cars.each do |car|
f = open("emoji_#{car}.rb", "w")
f.print <<EOT
Emoji_#{car} = {}
EOT
(0xF8..0xF9).each do |hi|
src = open("emoji_#{car}_#{"%02X"%hi}.rb", "r")
while line = src.gets
f.print line
end
src.close
end
f.close
end
このように。emoji_{vfone, ezweb}.rb が完成。
もちろんいきなりHashにせず
def emoji_list(car)
list = []
open("#{car}/index.htm") do |f|
while line = f.gets
if /(.*)<br>$/n =~ line
list.push $1
elsif /<img localsrc="(.*)" \/>/n =~ line
list.push [nil, $1.to_i]
elsif /(.*)<br \/>$/n =~ line
list.push $1
end
end
end
list
end
emoji_table = {}
%w/ i j ez /.each do |car|
emoji_table[car] = emoji_list(car)
end
emoji_table['i'].each_with_index do |imode, index|
vfone = emoji_table['j'][index]
ezweb = emoji_table['ez'][index]
imode.each_byte{|c| print "%02X" % c }
print "\t"
vfone.each_byte{|c| print "%02X" % c }
print "\t"
if ezweb[0].nil?
print "LS%d" % ezweb[1]
else
ezweb.each_byte{|c| print "%02X" % c }
end
print "\n"
end
このように 変換表 をまとめておけば後々有用でしょう。
ERBと半角カナ
半角カナを含む文字列をERBに処理させると化けます(たぶん tr や gsub で)。ので、ERBの出力をzen2hanで変換すればいいでしょう。
cgi.out(header){
zen2han(service.output, cgi.user_agent)
}
[ top ] [ prev ] [ up ] [ next ]