2 # Ruby/CHISE module by eto 2002-1114
8 $KCODE = 'u' #今のところこれ以外では動かない。String.splitが影響大。inspectも影響。
9 $debug = false #これはテスト用
10 $debug = true #これはテスト用
11 $stdout.binmode if $debug
12 $stdout.sync = true if $debug
14 class String #======================================================================
15 def to_a() self.split(//) end #$KCODEが設定されているので、UTF-8的な一文字づつがchにはいる
16 def each_char() to_a.each {|ch| yield ch } end
17 def each_character() to_a.each {|ch| yield ch.char } end
18 def char_length() to_a.length end
19 def char_at(n) to_a()[n] end
20 def char() Character.get(to_a[0]) end
22 def char_id() char.char_id() end
23 def get_char_attribute(a) char.get_char_attribute(a) end
24 #def ucs() char.ucs() end
31 def map_char(block = Proc.new)
32 return unless block_given?
33 return self.to_a.map {|ch| (block.call(ch)).to_s }.join("")
35 def map_char!(block = Proc.new)
36 return unless block_given?
37 self.replace(self.map_char {|ch| block.call(ch)})
39 def map_character(block = Proc.new)
40 return unless block_given?
41 return self.to_a.map {|ch| (block.call(ch.char)).to_s }.join("")
43 def map_character!(block = Proc.new)
44 return unless block_given?
45 self.replace(self.map_char {|ch| block.call(ch.char)})
48 def method_missing(mid, *args)
49 if char_length == 1 #省略形が有効なのは、一文字の時だけ
50 char.method_missing(mid, *args)
52 raise NameError, "undefined method `#{mid.id2name}'", caller(1)
56 def map_utf8() map_char {|ch| ch.char.map_utf8 } end
57 alias map_ucs map_utf8
58 def map_ucs_er() map_char {|ch| ch.char.map_ucs_er } end
59 def to_er() map_char {|ch| ch.char.to_er } end
61 #put関係、[]関係は用意しないことにした。
62 def de_er!() #EntityReferenceを取り除く
63 return self unless self =~ Regexp.new(EntityReference::REGEXP_PART) #それらしいのが無ければ何もしない
65 self.sub!(Regexp.new(Regexp.escape(er)), Character.new(er).mcs_utf8) #変換自体はCharacterにまかせる
66 return self.de_er! if self =~ Regexp.new(EntityReference::REGEXP_PART) #まだあったら再帰
69 def de_er() return self.dup.de_er!; end
71 def inspect_all() map_char {|ch| ch.char.inspect_all } end
72 def inspect_x() map_char {|ch| ch.char.inspect_x } end
74 def to_euc() map_char {|ch| ch.char.to_euc } end
75 def map_euc() map_char {|ch| ch.char.map_euc } end
76 def to_sjis() map_char {|ch| ch.char.to_sjis } end
77 def map_sjis() map_char {|ch| ch.char.map_sjis } end
79 def decompose() map_char {|ch| ch.char.decompose } end
80 def decompose!() self.replace(self.decompose); self; end
81 def decompose_all_nu(level=nil)
82 level = 0 if level.nil?
84 p ['too many recursive', self]
88 return de.decompose_all(level+1) if de != self #なにか変化があったから再帰
91 def decompose_all() map_char {|ch| ch.char.decompose_all } end
92 def decompose_all!() self.replace(self.decompose_all); self; end
94 def find() #"日雲"→"曇"とかいう感じの操作
96 length = char_length()
99 ar << char.ids_contained #その文字を含んでいる漢字のリスト
111 if length == v #全部に顔を出していたら
119 db = CodesysDB.instance
120 composed = db.get('ids', self)
121 return "" if composed.nil? #なかったよと。
122 return "" if composed.char_length == 0 #なにごと?
123 return composed if composed.char_length == 1
124 composed.each_char {|ch|
126 return ch if char.has_attribute? #とりあえず最初にみつかったものを返すというヌルい仕様
128 return "" #attributeを持つものが一つも無かったら、""にする
131 #selfである文字列をIDSだと仮定し、それを完全にcomposeしきらないで、
132 #その部分集合だけをとりだして、compose可能であればできるだけcomposeする。
133 tree = IDS_Tree.new(self)
134 return self if tree.depth <= 1 #sub_nodesが無い場合はここでさよなら
135 tree.sub_nodes.each {|node|
137 next if c.nil? || c == ""
138 # print "#{self} #{node} #{c}\n"
140 n = self.gsub(node, c)
143 return self #おきかえられるものがまったくなかったら、自分をかえす。
147 module CHISE #======================================================================
149 (RUBY_PLATFORM =~ /cygwin/ || RUBY_PLATFORM =~ /mswin32/ || RUBY_PLATFORM =~ /mingw32/)
151 module_function :windows?
153 DB_DIR = 'd:/work/chise/char-db' #この後に/sysmtem-char-id/ucsという感じに続く
155 DB_DIR = '/usr/local/lib/xemacs-21.4.10/i686-pc-linux/char-db' #この後に/sysmtem-char-id/ucsという感じに続く
158 class EntityReference #======================================================================
159 #状況によってどのERに変換するかが異なる可能性があるので、普通のclassとして実装したほうがいい?
161 %w( chinese-big5-cdp CDP- 4 X),
162 %w( ideograph-daikanwa M- 5 d),
163 %w( ideograph-cbeta CB 5 d),
164 %w( ideograph-gt GT- 5 d),
165 %w( ideograph-gt-k GT-K 5 d),
166 %w( japanese-jisx0208-1990 J90- 4 X),
167 %w( japanese-jisx0208 J83- 4 X),
168 %w( japanese-jisx0213-1 JX1- 4 X),
169 %w( japanese-jisx0213-2 JX2- 4 X),
170 %w( japanese-jisx0212 JSP- 4 X),
171 %w( japanese-jisx0208-1978 J78- 4 X),
172 %w( chinese-cns11643-1 C1- 4 X),
173 %w( chinese-cns11643-2 C2- 4 X),
174 %w( chinese-cns11643-3 C3- 4 X),
175 %w( chinese-cns11643-4 C4- 4 X),
176 %w( chinese-cns11643-5 C5- 4 X),
177 %w( chinese-cns11643-6 C6- 4 X),
178 %w( chinese-cns11643-7 C7- 4 X),
179 %w( korean-ksc5601 K0- 4 X),
181 CODESYS_ORDER = %w(japanese chinese korean ideograph)
182 REGEXP_PART = "&([-+0-9A-Za-z#]+);"
183 REGEXP_ALL = "^#{REGEXP_PART}$"
185 def self.match?(er) (er =~ Regexp.new(REGEXP_PART)) != nil end
187 def self.parse(er) #char_idをFIXNUMで返す
188 return "" unless er =~ Regexp.new(REGEXP_ALL) #なんか、間違ってる?
189 er = $1 #ついでに中身の部分を取り出す
190 return $1.hex if er =~ /^MCS-([0-9A-Fa-f]+)/ #MCS
191 # if er =~ /^U[-+]?([0-9A-Fa-f]+)/ #Unicode直打ち
192 if er =~ /^U[-+]?([0-9A-Fa-f]+)/ || er =~ /^#x([0-9A-Fa-f]+)/ #Unicode直打ち
196 er.sub!(/^I-/, '') if er =~ /^I-/ #I-がついてるとどう違うのかはよくわからない
197 each_codesys {|codesys, er_prefix, keta, numtype| #p [codesys, er_prefix, keta, numtype]
198 numtyperegex = '\d' #if numtype == 'd'
199 numtyperegex = '[0-9A-Fa-f]' if numtype == 'X'
200 regexpstr = "^#{er_prefix}(#{numtyperegex}{#{keta},#{keta}})$" #p regexpstr
201 if er =~ Regexp.new(regexpstr)
203 code = codestr.to_i #if numtype == 'd'
204 code = codestr.hex if numtype == 'X'
205 char_id_u8 = EntityReference.get_database(codesys, code)
206 char_id_num = Character.parse_char_id(char_id_u8)
213 def self.each_codesys()
214 CODESYS_ORDER.each {|lang|
215 CODESYS_TABLE.each {|codesys, er_prefix, keta, numtype| #普通こういう書き方はしない。ループ一個にする。
216 next unless codesys =~ lang
217 yield(codesys, er_prefix, keta, numtype)
221 def self.get_database(codesys, code)
222 c = CodesysDB.instance.get(codesys, code)
224 if codesys =~ /-jisx0208/
225 #return self.get_database("=jis-x0208", code) #再帰でどうだ?
226 c = CodesysDB.instance.get("=jis-x0208", code)
233 class CharacterFactory #============================================文字オブジェクトの生成、cache
242 n = Character.parse_char_id(char_id)
244 @chars[n] = Character.new(n) if @chars[n] == nil
250 GC.start #ガーベージコレクション
252 def length() @chars.length; end
254 reset if @max < @chars.length #MAXを超えたらresetしてしまう。乱暴じゃがcacheなのでこれでいいのだ。
258 class Character #=============================================================== 文字オブジェクト
259 BASIC_KANJI = "人子女母父王口耳手足力目首毛心犬牛鳥貝角羽虫馬魚羊肉皮米竹木麦豆山川雨風水土石金田穴日月火音糸刀舟門戸衣矢弓車皿一二三四五六七八九十百千万寸尺上中下本玉立回食行止交向歩考入示走生出来書言大小白青多少高長"
261 BASIC_KANJI.include?(self.to_s)
264 def initialize(char_id=nil)
265 @char_id = Character.parse_char_id(char_id)
266 @attributes = Hash.new
267 @check_all_database = false
270 def to_i() @char_id end
271 def mcs_utf8() Character.u4itou8(@char_id) end
272 def mcs_hex() sprintf("%x", @char_id) end
274 #----------------------------------------------------------------------
275 def self.get(char_id) CharacterFactory.instance.get(char_id) end #flyweightパターン
277 #----------------------------------------------------------------------
278 def normalize_attribute_name(b)
280 a.gsub!(/_/, '-') #underlineは-に置換
281 a.sub!(/^map-/, '=>')
283 a.sub!(/^from-/, '<-')
286 def get_char_attribute(b) # XEmacs UTF-2000互換API群
287 a = normalize_attribute_name(b)
290 return atr if atr != nil
291 atr = check_database(a)
292 @attributes[a] = atr if atr != nil
293 return get_char_attribute("=jis-x0208") if a =~ /jisx0208/ #ここだけ特殊形
294 return @attributes[a]
296 def put_char_attribute(b,v)
297 a = normalize_attribute_name(b)
299 CharDB.instance.put(a, mcs_utf8(), v)
301 def char_attribute_alist() check_all_database(); @attributes; end
302 def char_attribute_list() check_all_database(); @attributes.keys; end
303 alias [] get_char_attribute #その略称
304 alias []= put_char_attribute
305 alias alist char_attribute_alist
306 alias list char_attribute_list
308 def method_missing(mid, *args) #参考:ostruct.rb
310 return get_char_attribute(mname) if args.length == 0
311 put_char_attribute(mname.chop, args[0]) if mname =~ /=$/ #代入
314 def has_attribute?() #意味のあるattributeを持ってますか?
319 return (keys.length != 0)
322 #----------------------------------------------------------------------
324 return false if ch == nil
325 return false unless ch.is_a? Character
326 self.char_id == ch.char_id
329 #----------------------------------------------------------------------
330 def self.parse_char_id(char_id) #FIXNUMを返す
331 return nil if char_id == nil
332 if char_id.is_a?(Numeric) #p [char_id]
333 char_id = 0x80000000 + char_id if char_id < 0 #補数表現
335 elsif char_id.is_a?(String)
336 return char_id.to_i if char_id =~ /^\d+$/ && 1 < char_id.length #文字列による数字だったら数値化してreturn
337 return EntityReference.parse(char_id) if char_id =~ Regexp.new(EntityReference::REGEXP_ALL) #実体参照?
338 char_id.sub!(/^\?/, '') if char_id =~ /^\?/ #もし先頭に?がついていたら削除
339 #このへん本当はもっとちゃんとチェックするべし
341 u4 = Uconv.u8tou4(char_id) #UCS-4文字列に変換
347 return Character.u4tou4i(u4) #UCS-4数値にしてreturn
349 raise ArgumentError, "unknown object for char_id", caller(1)
353 return 0 if u4 == nil || u4 == ""
354 return (u4[3] << 24 | u4[2] << 16 | u4[1] << 8 | u4[0]) #UCS-4数値にしてreturn
356 def self.u4itou4(num)
357 return "" unless num.is_a?(Integer)
358 return sprintf("%c%c%c%c", num&0xff, (num >> 8)&0xff, (num >> 16)&0xff, (num >> 24)&0xff) #UCS-4数値を文字列にしてreturn
360 def self.u4itou8(char_id) #ucsの数値を受けとり、UTF-8の文字一文字を返す
362 u4 = Character.u4itou4(char_id)
363 u8 = Uconv.u4tou8(u4)
366 #raise ArgumentError, "invalid char_id (#{char_id})", caller(1)
372 #----------------------------------------------------------------------
373 def check_database(a)
376 v = db.get(a, u8) #u8で表される文字のaアトリビュートを調べる。
379 def check_all_database() #現在の@char_idから、文字データベースを参照する
380 return if @check_all_database
381 return if @char_id == nil
384 atrs = db.get_all(u8) #u8で表される文字のアトリビュートを全部持ってこい
386 @attributes[a] = v #とかいう感じで代入するのでええかな?
388 @check_all_database = true #重い処理なので一応checkする
391 #----------------------------------------------------------------------
393 #ar = %w{ucs ucs-big5 ucs-cdp ucs-cns ucs-jis ucs-ks =>ucs =>ucs* =>ucs-jis}
394 #ar = %w{ucs ucs-jis ucs-big5 ucs-cdp ucs-cns ucs-ks =>ucs =>ucs* =>ucs-jis}
395 ar = %w{ucs-jis ucs =>ucs-jis}
396 #並び順は恣意的で、ucs-jisを先に出している。本来はこれも指定できるようにするべき。
398 u = get_char_attribute(a)
404 #----------------------------------------------------------------------CCS関係
405 def to_utf8() Uconv.u4tou8(Character.u4itou4(ucs())) end #UTF8文字列を返す
410 if u.nil? || 0xffff < u
416 alias map_ucs map_utf8
419 if u.nil? || 0xffff < u
422 return Character.get(u).to_er()
427 return "" if u.nil? || 0xffff < u
428 Uconv.u16toeuc(Uconv.u4tou16(Character.u4itou4(ucs())))
437 return "" if u.nil? || 0xffff < u
438 Uconv.u16tosjis(Uconv.u4tou16(Character.u4itou4(ucs())))
446 #----------------------------------------------------------------------
447 def to_er(codesys=nil) #実体参照を返す、希望するcodesysが引数(未実装)
448 return "" if @char_id == nil
449 return sprintf("&#x%04x;", @char_id) if @char_id <= 0xffff
450 return sprintf("&#x%05x;", @char_id) if @char_id <= 0xfffff
451 EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
454 return sprintf("&#{er_prefix}%0#{keta}#{numtype};", code)
456 return sprintf("&MCS-%08X;", @char_id) #本当はこれは無しにしたい
460 EntityReference.each_codesys {|codesys, er_prefix, keta, numtype|
462 ar << er if er != nil
468 return "<>" if @char_id == nil
469 ar = [to_utf8(), to_er().sub(/^&/,'').chop]
472 alias inspect inspect_x
473 def inspect_all_codesys() #未完成
474 #to_erを全てのcodesysにおいて実行する。その結果をコンパクトにまとめる
478 alist.to_a.sort.each {|a, v| ar << "#{a}:#{v}" }
479 return ar.join(',')+">"
483 alist.to_a.sort.each {|a, v|
484 str += "#{a}: #{v}\n"
489 def inspect_ids(hex_flag=false)
492 ar << (hex_flag ? "x"+mcs_hex : to_utf8)
493 if to_s != ids #idsが部品そのものだったら部品追加はしない
498 ar << "x"+char.mcs_hex
509 return "("+ar.join("\t")+")"
512 #----------------------------------------------------------------------IDS関係
516 # return idss if idss
517 # return k if self.is_basic_kanji? #基本漢字はstop kanjiとするぞと。
518 return self['ids-represent'] if self['ids-represent'] #ids_representを持っている場合はその値とする。
519 return self['ids-element'] if self['ids-element'] #ids_elementを持っている場合はその値とする。
521 idss = self['ids-meaning']
522 return idss if idss != nil && 0 < idss.length && k != idss
523 idss = self['ids-aggregated']
524 return idss if idss != nil && 0 < idss.length && k != idss
526 return idss if idss != nil && 0 < idss.length && k != idss
528 # return k if idss.nil? || idss.length == 0 || k == idss
529 # if idss.char_length == 2
530 # p ['What???', k, idss, k.inspect_all]
531 # #return idssx[1] #二個目だけ返すとか?
532 # return k #IDSに展開する方法が無いと。
534 # return k if k == idss
535 # if idss.include?(k) #<C5-4C4D><C6-4A37>この二文字のBUG対策
536 # #return idss.sub(k, '')
537 # return k #IDSに展開する方法が無いと。
543 de = self.decompose #出発点
547 de = pde.decompose #もう一度分解をしてみる。
548 break if pde == de #ループを抜けだす
549 exit if 10 < level #p ['too many recursive', self]
554 def decompose_all_nu(level=nil)
555 level = 0 if level.nil?
557 p ['too many recursive', self]
561 return de.decompose_all(level+1) if de != self #なにか変化があったから再帰
562 return de #もうこれ以上変化は無さそうだぞと。
564 def is_ids?() 0x2ff0 <= @char_id && @char_id <= 0x2fff end
565 def ids_operator_argc()
566 return 0 unless is_ids?
567 return 3 if @char_id == 0x2ff2 || @char_id == 0x2ff3
572 class DBS #======================================================================複数のDBを集めたclass、未完成
575 class ADB < BDB::Hash #======================================================================一つのDB
576 def initialize(*args)
581 self.close #これがないと、うまくデータベースがセーブされないのです。
585 def self.open_create(filename)
586 ADB.open(filename, nil, BDB::CREATE | BDB::EXCL) #上書きはしない
590 if key.char_length == 1
591 return '?'+key #Stringだったら引く前に?を足す
594 #key = key.to_s if key.is_a?(Numeric) #NumberだったらStringにする。
595 #ここで && key ! =~ /^\?/ をいれると、?自身を検索できなくなってしまう。
600 return v.to_i if v =~ /^\d+$/ #数字だったらここで変換しておく
601 return v.sub(/^\?/, '') if v =~ /^\?/ #冒頭の?は取り除く
602 return $1 if v =~ /^"(.+)"$/ #最初と最後に"がついていたら、取り除く
603 #p ['get', v, t, key, db]
604 #return parse_sexp(v) if v =~ /^\(.+\)$/ #最初と最後が()の時は、S式にparseする
607 def myget(key) #keyキーを引いて返す
609 v = get(key) #存在しなかったらnilを返すことになる
612 def myput(key, v) #keyにvをいれる
619 class DB #======================================================= データベース群のabstract class
620 def self.unix_to_win(unix) #Windowsファイル名制限のため、変換する
621 win = unix.gsub(/</, '(')
627 def self.win_to_unix(win)
628 unix = win.gsub(%r|\)|, '>')
629 unix.gsub!(%r|\(|, '<')
630 unix.gsub!(%r|!|, '?')
631 unix.gsub!(%r|\+|, '*')
635 return @pre + DB.unix_to_win(t) + @post if windows?
636 return @pre + t + @post
638 def get_dirname(t) File.dirname(get_filename(t)) end
642 keys.each {|key| open_db(key) }
647 next if ! File.file?(f)
653 t = DB.win_to_unix(f)
654 t.sub!(%r|^#{@pre}|, '')
655 t.sub!(%r|#{@post}$|, '') if @post != ""
662 return nil if db.nil?
667 return nil if get(t) #すでにopenしていたら再openはしない。
669 bdb = ADB.open(get_filename(t), nil, 0)
670 @dbs[t] = bdb if bdb != nil
672 p ["open error", get_filename(t)]; return nil
676 def make_db(t, h=nil) #tという名前でhという中身のデータベースを作る
677 return nil if get(t) #すでにある場合はreturn
678 Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
681 db = ADB.open_create(get_filename(t)) #上書きはしない
684 k = '?'+k if k.is_a?(String)
690 p ["make error", get_filename(t)]; return nil
694 def make_db_no_question_mark(t, h=nil) #tという名前でhという中身のデータベースを作る
695 return nil if get(t) #すでにある場合はreturn
696 Dir.mkdir(get_dirname(t)) unless FileTest.exist?(get_dirname(t))
699 db = ADB.open_create(get_filename(t)) #上書きはしない
702 # k = '?'+k if k.is_a?(String)
708 p ["make error", get_filename(t)]; return nil
712 def remove_db(t) #tという名前のデータベースを消去する
719 File.unlink(get_filename(t)) if FileTest.file?(get_filename(t))
721 p ["unlink error", get_filename(t)]; return nil
724 Dir.rmdir(dn) if FileTest.directory?(dn) && Dir.entries(dn).length <= 2 #空directoryだったら消す
728 return s.to_i if s =~ /^\d+$/
734 file = get_filename(t)
735 open("#{file}.txt", "w"){|out|
736 # out.binmode.sync = true
738 ar.map! {|k, v| [to_num(k), to_num(v)] }
740 out.printf("%s\t%s\n", k, v)
745 def each_db() @dbs.to_a.sort.each {|t, db| yield(t, db) } end
746 def dump_all() each_db {|t, db| dump_db(t) } end
747 def close_all() each_db {|t, db| db.close } end
748 def keys() @dbs.keys end
750 return unless block_given?
756 k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
757 vv = get(t, k) #p ['each', t, k, v, vv]
762 return unless block_given?
766 ar.map! {|k, v| [to_num(k), to_num(v)] }
768 k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
769 vv = get(t, k) #p ['each', t, k, v, vv]
773 #----------------------------------------------------------------------
774 def get(t, key=nil) #tというデータベースのkeyキーを引いて返す
776 return db if key.nil?
780 def put(t, key, v) #tというデータベースのkeyにvをいれる
787 db.myput(key, v) #putする
791 class CharDB < DB #------------------------------------ MCS-UTF8をキーとした属性へのデータベース
795 @glob, @pre, @post = "#{DB_DIR}/system-char-id/*", "#{DB_DIR}/system-char-id/", ""
798 def get_all(u8) #全データベースのu8キーを引いてHashにまとめて返す
802 atrs[t] = v if v != nil
808 class CodesysDB < DB #----------------------------------------------------------------------
812 @glob, @pre, @post = "#{DB_DIR}/*/system-char-id", "#{DB_DIR}/", "/system-char-id"
815 #def keys() @dbs.keys.sort end #どんなCodesysの情報を持っているかの一覧
816 def keys() @dbs.keys end #どんなCodesysの情報を持っているかの一覧
820 return Codesys.new(t)
824 class Codesys < DB #======================================================================
828 @dbs = CodesysDB.instance
830 def keys() #どんなコードポイントの情報を持っているかの一覧
831 ks = @dbs.get(@name).keys
832 if @name =~ /jisx0208/ #特別処理
833 n = @dbs.get('=jis-x0208').keys
834 # p ['keys', @name, ks, n]
837 ks.map! {|k| to_num(k) }
841 v = @dbs.get(@name, key)
843 if @name =~ /jisx0208/ #jisx0208が含まれている場合だけ特別処理する
844 return @dbs.get('=jis-x0208', key)
849 return unless block_given?
855 k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
856 vv = @dbs.get(@name, k) #p ['each', t, k, v, vv]
861 return unless block_given?
865 ar.map! {|k, v| [to_num(k), to_num(v)] }
867 k.sub!(/^\?/, '') if k =~ /^\?/ #冒頭の?は取り除く
868 vv = @dbs.get(@name, k) #p ['each', t, k, v, vv]
876 db = CodesysDB.instance
877 @common = db.get_codesys('=jis-x0208')
878 @newest = db.get_codesys('japanese-jisx0208-1990')
881 char = @common.get(code)
882 return char unless char.nil?
883 char = @newest.get(code)
884 return char unless char.nil?
889 class IDS_TEXT_DB < DB #======================================================================
892 IDS_DB_DIR = 'd:/work/chise/ids/' #この後にIDS-JIS-X0208-1990.txtという感じに続く
894 IDS_DB_DIR = '/home/eto/work/chise/ids/' #この後にIDS-JIS-X0208-1990.txtという感じに続く
898 #IDS-UCS-Compat-Supplement.txt
907 IDS-JIS-X0208-1990.txt
928 @glob, @pre, @post = "#{IDS_DB_DIR}/db/*", "#{IDS_DB_DIR}/db/", ""
929 dir = File.dirname(@pre)
930 Dir.mkdir(dir) unless FileTest.exist?(dir)
934 return unless block_given?
935 @ids_list.each {|file|
937 yield(IDS_DB_DIR+file)
943 next if line =~ /^;/ #コメントはとばす
945 code, char, ids = line.split
946 yield(code, char, ids)
952 dir = File.dirname(file) + '/../ids-new/'
953 Dir.mkdir(dir) if ! FileTest.directory?(dir)
954 newfile = dir + File.basename(file)
956 open(newfile, "w"){|out|
957 out.binmode.sync = true
958 each_line(file){|code, ch, ids|
959 char = Character.get(ch)
961 out.print "#{code} #{ch} #{ids}\n"
968 dir = File.dirname(file) + '/../ids-error'
969 Dir.mkdir(dir) unless FileTest.exist?(dir)
970 errfile = dir + '/' + File.basename(file)
972 open(errfile, "w"){|out|
973 out.binmode.sync = true
974 each_line(file){|code, ch, ids|
975 char = Character.get(ch)
976 ids_error = char['ids-error']
977 next if ids_error.nil?
978 out.print "#{code} #{ch} #{ids} #{ids_error}\n"
985 class IDS_DB < DB #======================================================================BDB化したIDS DBを扱う
988 @dbs = CharDB.instance
991 db = IDS_TEXT_DB.instance
994 @same_ids_counter = 0
995 @good_ids_counter = 0
996 @conflict_ids_counter = 0
997 db.each_line(file){|code, ch, ids|
1000 ids = "" if ids == nil
1001 next if ids == "" #IDSが定義されていない場合は、さっくりと無視するべしよ。
1003 charimg = Character.get(ch) #実体参照である可能性がある
1005 next if code =~ /'$/ || code =~ /"$/ #大漢和番号のダッシュ付きは無視する
1006 char = Character.get("&"+code+";") #code表記を元に実体参照を作って解釈する
1007 if char.nil? || char.to_s == "" #うまく文字にならなかった
1008 print "char == null #{char.inspect} #{code} #{ch} #{ids}\n" unless code =~ /^M-/ || code =~ /^CB/
1009 #大漢和、CBETA以外の場合は、エラーメッセージ。
1012 if char != charimg #code表記と文字が一致していない?
1013 unless code =~ /^M-/ || code =~ /^MH-/ || code =~ /^CB/ #食い違っていて当然であるので何もしない
1014 print "unknown char #{char.inspect} #{code} #{ch} #{ids}\n"
1015 next #それ以外の場合はエラーメッセージをだして、次へ。
1018 #next if !char.has_attribute? #isolated characterはまぎれこませない。
1020 ids.de_er! #実体参照を解除する
1021 next if ids == char.to_s #もし文字とまったく一緒なら、意味が無いので情報を持たない
1022 next if ids.char_length == 1
1024 idstree = IDS_Tree.new(ids)
1025 c = idstree.check_integrity
1026 c = "contains self" if ids.include?(char.to_s)
1027 if c #ちょっとでもエラーがある場合は、
1028 char['ids-error'] = c #エラーを記録して、データとしては保持しない
1032 if char['ids'].nil? || char['ids'] == "" #元々IDSが無かった場合は、
1033 char['ids'] = ids #普通に代入すればそれでいいです。
1034 @good_ids_counter += 1
1035 else #しかしいままでにすでにIDSが定義されていた場合は?
1036 if char['ids'] == ids #新しいIDSと古いIDSが完全に一致するなら無視しましょう。
1037 @same_ids_counter += 1
1038 else #しかしいままでのIDSと新しいIDSが食い違った場合は?
1039 @conflict_ids_counter += 1
1040 # print "conflict #{char.inspect} #{code} #{ids} #{char['ids']}\n"
1044 print "#{file} #{@char_counter} #{@same_ids_counter} #{@conflict_ids_counter} #{@good_ids_counter}\n"
1045 CharacterFactory.instance.reset()
1047 @dbs.dump_db('ids-error') #テキスト化する
1048 @dbs.dump_db('ids') #テキスト化する
1050 def make_ids_reverse
1052 @dbs.each('ids') {|k, v|
1054 ids = char.decompose
1055 h[ids] = "" if h[ids].nil?
1059 h[k] = char_sort(v) #文字の順番を、よく使うっぽいものからの順番にする
1061 h.delete_if {|k, v| #h[k]が""になる可能性もあるが、それはkeyとして入れないことにする。
1064 print "length #{h.length}\n"
1065 cdb = CodesysDB.instance
1066 cdb.make_db_no_question_mark('ids', h)
1067 cdb.open_db('ids') #これが無いと、dump_dbされません。
1070 def char_sort(composed)
1071 return composed if composed.char_length == 1
1077 if char.char_id < 0xfffff #Unicodeっぽい?
1083 EntityReference.each_codesys{|codesys, er_prefix, keta, numtype|
1087 # p [codesys, v] if v
1088 if v #EntityReferenceの順番に準拠する。
1096 # p ['yokuwakaran character', ar, ar[0].inspect_all, arorg]
1097 EntityReference.each_codesys{|codesys, er_prefix, keta, numtype|
1101 # p [codesys, v] if v
1107 def dump_ids_duplicated
1108 open('ids-duplicated.txt', 'w'){|out|
1110 CodesysDB.instance.each('ids') {|k, v|
1112 out.print "nil #{k} #{v}\n"
1117 out.print "#{n} #{k} #{v}"
1120 out.print " #{char.inspect}"
1126 def make_ids_aggregated
1127 @dbs.each('ids') {|k, v|
1129 ids = char.decompose
1131 char['ids-aggregated'] = ag
1133 @dbs.dump_db('ids-aggregated')
1135 def dump_ids_aggregated
1136 open('ids-aggregated.txt', 'w'){|out|
1138 @dbs.each('ids') {|k, v|
1141 ag = char['ids-aggregated']
1142 out.print "#{char.to_s} #{ag} #{ids}\n" if ids != ag
1147 @dbs.each('ids') {|k, v|
1153 ids = pids.decompose
1154 break if ids == pids #これ以上分割できないようだったら終了〜。
1157 p [char.to_s, pids, ids, ar] if 10 < counter #これは何かおかしいぞと
1162 #やっぱりIDS文字も加えることにする. by eto 2003-02-05
1163 # ar.delete_if {|ch|
1164 # ch.char.is_ids? #IDS文字はまぎれこませない。
1167 char['ids-parts'] = str
1169 @dbs.dump_db('ids-parts')
1171 def make_ids_contained
1173 @dbs.each('ids-parts') {|k, v|
1175 parts = char.ids_parts
1176 parts.each_char {|ch|
1178 h[ch] = [] if h[ch].nil?
1181 # part['ids-contained'] = "" if part['ids-contained'].nil?
1182 # part['ids-contained'] += k
1188 char['ids-contained'] = v.join('')
1191 @dbs.dump_db('ids-contained')
1193 def make_ids_decomposed
1194 @dbs.each('ids') {|k, v|
1196 de= char.decompose_all
1197 char['ids-decomposed'] = de
1199 @dbs.dump_db('ids-decomposed')
1203 class Node < Array #=======================================================木構造の中の一つの枝
1204 def initialize(nodeleaf=nil, nodenum=nil)
1206 @nodeleaf = nodeleaf
1209 original_add(@nodeleaf)
1212 attr_reader :nodenum
1213 alias original_add <<
1214 private :original_add
1217 @nodenum -= 1 if @nodenum
1223 ar += n.nodes if n.is_a? Node
1229 class Tree #======================================================================木構造を扱う
1234 @depth = 1 #stackの深さが最大になったところの値、木構造が無いときは1となる
1236 def depth() @depth - 1 end
1237 def add_node(nodeleaf=nil, nodenum=nil) #枝を追加
1238 new_node = Node.new(nodeleaf, nodenum)
1239 @stack.last << new_node
1241 if @depth < @stack.length
1242 @depth = @stack.length
1246 def end_node() #この枝は終り
1250 def add_leaf(a) #葉を追加
1256 n = @stack.last.nodenum
1263 n = @stack.last.nodenum
1264 return nil if @root.length == 0 #no tree is good tree
1265 return "unmatch leaves" if n && n != 0
1266 return "extra nodes" if @root.first.is_a?(Node) && @root.length != 1
1267 return "extra leaves" if @root.length != 1
1280 def to_s() @root.to_s end
1281 def inspect() @root.inspect end
1284 class IDS_Tree < Tree #======================================================================
1291 @str.each_char {|ch|
1292 char = Character.new(ch)
1294 add_node(char, ids_operator_argc(char))
1301 return true if "+*".include?(obj.to_s) #テスト用ですかね
1302 return true if obj.is_ids?
1305 def ids_operator_argc(obj)
1306 return obj.ids_operator_argc if 0 < obj.ids_operator_argc
1311 return r if r #不完全がすでにわかっているならreturn
1312 return "contains ques" if @str =~ /\?/ #?が含まれている?
1318 class IDS #======================================================================IDSそのものを扱うclass
1319 def initialize(str) #IDS文字列をうけとる。
1324 def parse_x #柔軟型のParse. IDSキャラクターが前にきてなくてもよい。などなど。
1328 class Counter #======================================================================
1330 #counter = Counter.new(50) { exit }
1345 class DBS_Management #======================================================================ファイル管理
1346 OBSOLETE_ATTRIBUTES = "
1377 ancient-ideograph-of
1378 ancient-char-of-shinjigen-1
1379 original-ideograph-of
1380 original-char-of-shinjigen-1
1381 simplified-ideograph-of
1383 vulgar-char-of-shinjigen-1
1385 ideographic-variants
1386 variant-of-shinjigen-1
1391 @odir = DB_DIR+"/system-char-id/obsolete" #直打ちしている。
1393 def move_obsolete_files # 廃止予定のbdbファイルをobsoleteディレクトリーにつっこむ
1394 db = CharDB.instance
1396 Dir.mkdir(@odir) unless FileTest.directory? @odir
1397 OBSOLETE_ATTRIBUTES.each {|attr|
1398 next if attr =~ /^#/
1399 filename = db.get_filename(attr)
1400 move_to_obsolete(filename)
1401 move_to_obsolete(filename+".txt")
1404 def move_to_obsolete(file)
1405 cmd = "mv #{file} #{@odir}"
1411 class JoyoList #======================================================================
1413 #JP_JOYO_FILE = DB_DIR+"/../jp-joyo.txt" #EUC-jisx0213
1414 JP_JOYO_FILE = DB_DIR+"/../joyo-ucs.txt" #UCS
1415 COMPOSIT_KANJI = "鳴名加品古知問間聞取兄見切分粉貧林森校東明住位好岩砂里男畑習休短空坂島倉美孝赤看光初努協解新歌語話張強忘悲答晴現正字安守灰秋秒困国医包同合舌居右左受友反道返迷花菜集机主太氷州点店庫仕帳幼防引配早直班筆重番北化比死夏後進酒福私家世内谷半原前寺思電雲気布旅衆泣"
1416 # COMPOSIT_KANJI = "鳴名加品古"
1423 open(JP_JOYO_FILE) {|f|
1425 next if line =~ /^;/ #コメントはとばす
1427 #stroke, nchar, ochar = line.split #new char, old char, old charはnilが多い
1428 stroke, nchar = line.split
1436 print char.inspect_ids(true), "\t;", char.inspect_ids(false), "\n"
1443 #----------------------------------------------------------------------終了