Rubyで康煕部首・CJK部首補助を検出、置換する

Rubyで康煕部首・CJK部首補助を検出、置換するものをとりあえず作ってみた。

文字の変換表は、PythonでCJK部首/康熙部首を置換 - メモ が公開してくれているものを参考にした。

--

使い方はこんな感じ

require "./lib/string_ja.rb"
using StringJa::Radical

"埼玉埼⽟問題".codepoints # => [22524, 29577, 22524, 12127, 21839, 38988]
"埼玉埼⽟問題".include_kangxi_radicals? # => true
"埼玉埼⽟問題".tr_kangxi_radicals.codepoints # => [22524, 29577, 22524, 29577, 21839, 38988]

コード全体は以下*1

# Stringクラスを日本語用に拡張するモジュール
module StringJa
    # 部首を扱うモジュール
    module Radical
  
       # 康煕部首のCODEPOINT
       KANGXI_RADICALS_REGEX_PATTERN=/[\u2F00-\u2FDF]/

       # CJK部首補助のCODEPOINT
       CJK_RADICALS_SUPPLEMENT_REGEX_PATTERN=/[\u2E80-\u2EFF]/
       
       # 康煕部首のうち置換する文字の一覧
       # via https://imabari.hateblo.jp/entry/2020/08/03/220407
       # 
       # CODE Pointsを確認したいときは、StringJa::Radical::KANGXI_RADICALS_PATTERN.each_char {|c| puts "#{c} : #{c.codepoints[0].to_s(16)}" }
       KANGXI_RADICALS_PATTERN="⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕"
       
       # 康煕部首から置換可能な文字の一覧
       # via https://imabari.hateblo.jp/entry/2020/08/03/220407
       KANGXI_RADICALS_REPLACEMENT="一丨丶丿乙亅二亠人儿入八冂冖冫几凵刀力勹匕匚匸十卜卩厂厶又口囗土士夂夊夕大女子宀寸小尢尸屮山巛工己巾干幺广廴廾弋弓彐彡彳心戈戸手支攴文斗斤方无日曰月木欠止歹殳毋比毛氏气水火爪父爻爿片牙牛犬玄玉瓜瓦甘生用田疋疒癶白皮皿目矛矢石示禸禾穴立竹米糸缶网羊羽老而耒耳聿肉臣自至臼舌舛舟艮色艸虍虫血行衣襾見角言谷豆豕豸貝赤走足身車辛辰辵邑酉釆里金長門阜隶隹雨靑非面革韋韭音頁風飛食首香馬骨高髟鬥鬯鬲鬼魚鳥鹵鹿麥麻黃黍黒黹黽鼎鼓鼠鼻齊齒龍龜龠"
  
      # CJK部首補助のうち置換する文字の一覧
      # via https://imabari.hateblo.jp/entry/2020/08/03/220407
      CJK_RADICALS_SUPPLEMENT_PATTERN='⺃⺅⺉⺋⺎⺏⺐⺒⺓⺔⺖⺘⺙⺛⺟⺠⺡⺢⺣⺦⺨⺫⺬⺭⺱⺲⺹⺾⻁⻂⻃⻄⻍⻏⻑⻒⻖⻘⻟⻤⻨⻩⻫⻭⻯⻲'
      
      # CJK部首補助から置換可能な文字の一覧
      # via https://imabari.hateblo.jp/entry/2020/08/03/220407
      CJK_RADICALS_SUPPLEMENT_REPLACEMENT='乚亻刂㔾兀尣尢巳幺彑忄扌攵旡母民氵氺灬丬犭罒示礻罓罒耂艹虎衤覀西辶阝長镸阝青飠鬼麦黄斉歯竜亀'

      refine String do
        #文字列に康煕部首が含まれているか
        def include_kangxi_radicals?
          self.match?(KANGXI_RADICALS_REGEX_PATTERN) 
        end
  
        #文字列に含まれる康煕部首を可能な限り漢字に置き換える
        def tr_kangxi_radicals
          self.tr(KANGXI_RADICALS_PATTERN, KANGXI_RADICALS_REPLACEMENT)
        end

       #文字列に含まれる康煕部首を可能な限り漢字に破壊的に置き換える
        def tr_kangxi_radicals!
          self.tr!(KANGXI_RADICALS_PATTERN, KANGXI_RADICALS_REPLACEMENT)
        end
  
        #文字列にCJK部首補助が含まれているか
        def include_cjk_radicals_supplement?
          self.match?(CJK_RADICALS_SUPPLEMENT_REGEX_PATTERN)
        end
  
        #文字列に含まれるCJK部首補助を可能な限り漢字に置き換える
        def tr_cjk_radicals_supplement
          self.tr(CJK_RADICALS_SUPPLEMENT_PATTERN, CJK_RADICALS_SUPPLEMENT_REPLACEMENT)
        end

        #文字列に含まれるCJK部首補助を可能な限り漢字に破壊的に置き換える
        def tr_cjk_radicals_supplement
          self.tr(CJK_RADICALS_SUPPLEMENT_PATTERN, CJK_RADICALS_SUPPLEMENT_REPLACEMENT)
        end
      end
    end  
end

ちょっと時間がないのでいったんここまでだが、時間ができたらテストを足してGemにしたい。

参考

メモ

グリフとかcmapとか知らなかった。すごい雑にいうと

  • グリフ: フォントにおける1文字の字形
  • cmap: 文字コードとグリフの対応表

であって、 「康熙部首」と「CJK部首補助」が意図せず混入するのは、文字コードからグリフに変換した後、逆にグリフから文字コードに変換するときに別の文字コードに変換されることがあるからだそう。

プログラミングまとめ|みし|noteのフォントを読むシリーズも勉強になりそうだが、これはあとで。

またRubyのRefinementの仕組みも恥ずかしながら今回初めて知ったのでこれも勉強になった。

RubyのRefinement(翻訳: 公式ドキュメントより)|TechRacho by BPS株式会社

*1:シンタックスハイライトするとなぜか全部表示できないので、ハイライトなしで