[ 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)
  }
author: YOSHIDA Kazuhiro
[ top ] [ prev ] [ up ] [ next ]