# -*- coding: utf-8 -*-
"""
Created on Fri Oct 4 16:52:50 2024
@author: Arnd Helmut Hafner
"""
import sys
import re
from re import compile, search
import tools
class Div:
"""
一つの標題行と複数の本文行から構成される一つの事案を一つの
オブジェクトに纏めるためのクラス。
初期化に当たって、標題行を収めるHeadクラスと本文を収めるHonbunクラスが呼び出されるから、
このクラスの中で両者は結合される。
dicは、TEI-Encoder(shakudoku02)の冒頭で初期化された、漢字のglyphエンコーディング用の辞書を
オブジェクトとして引き渡す。
tagsはtags.pyにおいて生成されたタグの辞書を渡す。
"""
divcount = -1
def __init__(self,head,dic,tags):
self.head = Head(head,dic,tags)
self.honbun = Honbun(dic,tags)
self.tags = tags
self.zenbun = self.tags.tagdict['divb']
type(self).divcount += 1
def complete(self):
"""
Divオブジェクトを組み立てる。
Divオブジェクトの初期化にあたり、すでに、"
"がself.zenbunに収められた。
ここでは、エンコーディングされた標題行と本文行を順番に追加していき、
"
"で締めくくる。
Returns
-------
str
self.zenbun(一事案の全文をエンコーディングされた形で含む)
"""
self.zenbun += self.head.head
for n in range(len(self.honbun.honbun)):
self.zenbun += self.honbun.honbun[n]
self.zenbun = re.sub('\n$',self.tags.tagdict['pe'] + self.tags.tagdict['dive'] + '\n\n',self.zenbun)
return
class Mojiretu:
"""
文字列を文字単位でエンコーディングするためのファンクションを収めたクラス。
『為獄等状四種』の場合には、整理者によって付与された標題はすべてのエディションにおいて変わらないから
ほとんど関係がないが、標題行のエンコーディングにも利用できるように、HeadとHonbunの二つのクラスの親クラスに設定しておいた。
"""
def __init__(self):
pass
#@staticmethodは切り離し、tools.pyに収めた
def appabbr(self,abbr,expand):
"""
重文記号を含む文字列をエンコーディングする。
Parameters
----------
abbr : str
白文のままの文字列("字="や"字=字="等)
expand : str
重文記号を解釈して展開した釈文の文字列(丸括弧内の文字列、場合によっては句読点を含む)
Returns
-------
firstchar : str
エンコーディングされた文字列
"""
#省略形のタグ付け
firstchar = self.tags.tagdict['choiceb'] + self.tags.tagdict['abbrb']
#"字=字="を"字="と"字="に分解する
abbrbunkai = re.findall('(?:{[^}]+}|[^()&﹦])(?:(?))?&?﹦&?',abbr)
for p in range(len(abbrbunkai)):
kashira = re.findall('^(?:{[^}]+}|.)',abbrbunkai[p])[0]
#"字&="への対応
if re.search('^(?:{[^}]+}|[^﹦])&',abbrbunkai[p]):
kashira = tools.suppliedillegchar(kashira)
#"字(?)"への対応
elif re.search('^(?:{[^}]+}|[^﹦])(?)',abbrbunkai[p]):
kashira += self.tags.tagdict['certaintyreading']
firstchar += kashira
if re.search('﹦&',abbrbunkai[p]):
firstchar += self.tags.tagdict['amb'] + tools.suppliedillegchar(self.dic.glyphdict['﹦']) + self.tags.tagdict['ame']
else:firstchar += self.tags.tagdict['amb'] +self.dic.glyphdict['﹦'] + self.tags.tagdict['ame']
firstchar += self.tags.tagdict['abbre'] + self.tags.tagdict['expanb']
#処理済みの"&"や"(?)"を削除する
abbr = re.sub('[&﹦]','',abbr)
abbr = re.sub('(?)','',abbr)
#拡張形のタグ付け
#展開された文字列の中から、重文記号を除いた白文に対応する部分を検出し("A="や"A=B="の"A"や"AB"等)
#展開によって生じた部分と区別してそれぞれexpandichijiとexに格納する
if re.search('^(?:{[^}]+}|[^{]){2,}$',abbr) and '-' not in expand:#'肆﹦室﹦(肆、室。肆、室)'等に対応する
abbr2 = tools.searchpreparation(abbr,'[。,、?!:;]?')
elif '-' in expand:#'气﹦鞫﹦(气-乞鞫,气-乞鞫)'等に対応する
abbr2 = tools.searchpreparation(abbr,'(?:\\-.)?','','(?:\\-.)?')
else:abbr2 = abbr
regex = compile('(' + abbr2 + '[。,、?!:;]?)')
m1 = re.search(regex,expand)
if m1:
expandichiji = m1.group(1)
ex = re.sub(regex,'',expand,1)
else:sys.exit('Mismatch in appabbr for {}\nbango is {}\n'.
format(expand,self.bango[-1]))
#expandichijiとexのエンコーディング
if '-' in expand:
#"字-字"の処理
expandichiji = tools.hyphenjokyo(expandichiji,self.tags.tagdict['origb'],self.tags.tagdict['orige'])
ex = tools.hyphenjokyo(ex,self.tags.tagdict['exb'],self.tags.tagdict['exe'])
#
if re.search('^[^<]',ex):okikae = compile('^([^<])')
else:okikae = compile('([^>])$')
ex = okikae.sub('{}\\g<1>{}'.format(self.tags.tagdict['exb'],self.tags.tagdict['exe']),ex)
else:
ex = self.tags.tagdict['exb'] + ex + self.tags.tagdict['exe']
#句読点のエンコーディング(
との間のみ。の間にはは意味をなさない)
expandichiji = tools.kutoencoding(expandichiji)
#expandichijiとexを組み立てる
if abbr[0] == expand[0]:
firstchar += expandichiji + ex + self.tags.tagdict['expane'] + self.tags.tagdict['choicee']
else:#'夫=(大夫)'
firstchar += ex + expandichiji + self.tags.tagdict['expane'] + self.tags.tagdict['choicee']
#句読点の位置を調整とエンコーディング
if re.search('[^<]+[。,、?!:;]',firstchar):
regex = compile('([^<]+)([。,、?!:;])')
firstchar = regex.sub('\\g<1>\\g<2>',firstchar)
m = re.search('([。,、?!:;])',firstchar)
if m:
okikaego = tools.kutoencoding(m.group(0))
firstchar = re.sub(m.group(0),okikaego,firstchar)
return firstchar
def hyotenoriencoding(self,hyotenori,hatena,kuto):
"""
原文標点("●"と"┘")のエンコーディングを行う。
後続の句読点や釈読の不確実性を表す疑問符を規準に扱いを決める。
Parameters
----------
hyotenori : str
原文標点
hatena : str
疑問符が検出された場合にそれを格納する。
(句読点として追加されたものと、釈読に対する疑問を表すものとが混在する)
kuto : str
句読点
Returns
-------
hyotenori : str
エンコーディングされた原文標点
"""
shori = ''
#句読点との組み合わせのチェック。
#釈読を現わす疑問符が検出される時はその場で''を挿入する
if hatena and not kuto:#"●(?)"と"┘(?)"
if 'Eluji' in hyotenori and self.maeji not in '[。,、?!:;【]' and self.maeji != '':#"字┘(?)"
shori,kuto = 'choiceorireg',hatena
else:#"●(?)"および行頭もしくは文末の"┘(?)"
hyotenori += self.tags.tagdict['certaintyreading']#"●(?)"
shori = 'no additional tag'
elif hatena and kuto:#"●(?+句読)", #"┘(?+句読)"
hyotenori += self.tags.tagdict['certaintyreading']
shori = 'choiceorireg'
elif kuto:#"●(句読)","┘(句読)"
shori = 'choiceorireg'
elif self.maeji not in '[。,、?!:;【]' and self.maeji != '':#"字●字","字┘字"
shori = 'surplusreg'
else:#"句読点●字","句読点┘字"や行頭の"●"と"┘"
shori = 'no additional tag'
#タグ付け
if shori == 'no additional tag':
pass
elif shori == 'choiceorireg':
hyotenori = self.tags.tagdict['choiceb'] + self.tags.tagdict['origb'] + hyotenori + self.tags.tagdict['orige'] + self.tags.tagdict['regb'] + kuto + self.tags.tagdict['rege'] + self.tags.tagdict['choicee']
elif shori == 'surplusreg':
hyotenori = self.tags.tagdict['surplusreg'] + hyotenori + self.tags.tagdict['surpluse']
else:
sys.exit('mismatch in hyotenoriencoding for {}'.format(self.bango[-1]))
return hyotenori
def yomikaeencoding(self,yomikaemae,illegible,yomikaego,tag1,tag2):
"""
タグの種類と順番がやや異なる以外全てTEI-Encoder(shakudoku01).py.yoomikaeencodingに同じ
故にyomikaeとyomikaegoという変数名も踏襲した。
狭義の読み換えと誤字の処理は基本的に同じであるから、両者を一つに纏めた。
Parameters
----------
yomikaemae : str
"A(B)"の"A"
illegible : str
囲い文字を表す"&"
yomikaego : str
"A(B)"の"B"
tag1 : str
狭義の読み換えなら,誤字の場合は
tag2 : str
狭義の読み換えなら,誤字の場合は
Returns
-------
str
エンコーディングされた文字列
"""
#?の処理
if '?' in yomikaego:yomikaemae,yomikaego = tools.hatenashori(yomikaemae,yomikaego)
#&の処理
if illegible:yomikaemae = tools.suppliedillegchar(yomikaemae)
#エンコーディング
if '-' in yomikaego:
ichiji = yomikaego.split('-')
baikaiji,yomikaego = ichiji[0],ichiji[1]
#決まって二つの値しかないから、次のようにできるはずだが、too many values to unpack (expected 2)というエラーを惹起する
#baikaiji,yomikaego = yomikaego.split('-')
return self.tags.tagdict['choiceb'] + self.tags.tagdict['origb'] + self.tags.tagdict['choiceb'] + self.tags.tagdict['sicb'] + yomikaemae + self.tags.tagdict['sice'] + tag2 + baikaiji + re.sub('<','',tag2) + self.tags.tagdict['choicee'] + self.tags.tagdict['orige'] + self.tags.tagdict['regb'] + yomikaego + self.tags.tagdict['rege'] + self.tags.tagdict['choicee']
#choiceb + origb + choiceb +
#sicb + yomikaemae + sice +
#tag2 + baikaiji + re.sub('<','',tag2) + choicee + orige +
#regb + yomikaego + rege + choicee
else:
return self.tags.tagdict['choiceb'] + tag1 + yomikaemae + re.sub('<','',tag1) + tag2 + yomikaego + re.sub('<','',tag2) + self.tags.tagdict['choicee']
#choiceb +
#tag1 + yomikaemae + re.sub('<','',tag1) +
#tag2 + yomikaego +re.sub('<','',tag2) + choicee
def chopfirstchar(self):
"""
異なる文字列単位を検出し、エンコーディング処理を行う。
"""
#重文記号の検出("字=(字字)", "字=字=(字字,字字"等
m = re.search('^((?:(?:{[^}]+}|[^()&﹦])(?:(?))?&?﹦&?)+)(([^)]+))',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
value = Mojiretu.appabbr(self,m.group(1),m.group(2))
self.maeji = m.group(0)
return value
#二行に跨る重文記号処理("字=\n字=(字字,字字")
m = re.search('^((?:{[^}]+}|[^()&﹦])(?:(?))?&?﹦&?)$',self.text)
if m:
self.zanyo = m.group(0)
self.text = ''
return ''
#原文標点記号の処理("●", "┘")
m = re.search('^([●┘])(&?)(?(??)([。,、?!:;]?))?',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
hyotenori = self.dic.glyphdict[m.group(1)]
if m.group(2):hyotenori =tools.suppliedillegchar(hyotenori)#"●&"と"┘&"
value = Mojiretu.hyotenoriencoding(self,hyotenori,m.group(3),m.group(4))
self.maeji = m.group(0)
return value
#釈読に対する疑問("字(?)")("●(?)", "┘(?)"は処理済み)
m = re.search('^(.)(&?)(?)',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
if m.group(2):value = tools.suppliedillegchar(m.group(1))
else:value = self.maeji = m.group(1)
return value + self.tags.tagdict['certaintyreading']
#衍字("[字]")
m = re.search('^[([^]]+)]',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
self.maeji = m.group(0)
return self.tags.tagdict['surplusred'] + m.group(1) + self.tags.tagdict['surpluse']
#読み換え("字(字)")
m = re.search('^({[^}]+}|.)(&?)(([^)]+))',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
value = Mojiretu.yomikaeencoding(self,m.group(1),m.group(2),m.group(3),self.tags.tagdict['origb'],self.tags.tagdict['regb'])
self.maeji = m.group(0)
return value
#誤字("字〔字〕")
m = re.search('^({[^}]+}|.)(&?)〔([^)]+)〕',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
value = Mojiretu.yomikaeencoding(self,m.group(1),m.group(2),m.group(3),self.tags.tagdict['sicb'],self.tags.tagdict['corrb'])
self.maeji = m.group(0)
return value
#未釈読字("□")
if re.search('^□',self.text):
self.text = re.sub('□','',self.text,1)
self.maeji = '□'
return self.tags.tagdict['gap1']
#TEI-Encoder(shakudoku01)と違い、各未釈読字を単独で処理する。その方が後のテキスト比較が容易になると考えられる。
#未釈読字("……")
if re.search('^……',self.text):
self.text = re.sub('……','',self.text,1)
self.maeji = '……'
return self.tags.tagdict['gap2']
#脱文("〖字〗")
if re.search('^〖',self.text):
if self.omission:sys.exit('Mismatch at beginning of omission passage for {}'.format(self.bango))
self.text = re.sub('〖','',self.text,1)
self.omission = True
self.maeji = '〖'
return self.tags.tagdict['suppliedomb']
if re.search('^〗',self.text):
if not self.omission:sys.exit('Mismatch at end of omission passage for {}'.format(self.bango))
self.text = re.sub('〗','',self.text,1)
self.omission = False
self.maeji = '〗'
return self.tags.tagdict['suppliede']
#断簡処理("〼"→"【…】")
if re.search('^【',self.text):
if self.damage:sys.exit('Mismatch at beginning of damage passage for {}'.format(self.bango))
self.text = re.sub('【','',self.text,1)
self.damage = True
self.maeji = '【'
return self.tags.tagdict['supplieddamb']
if re.search('^】',self.text):
if not self.damage:sys.exit('Mismatch at end of damage passage for {}'.format(self.bango))
self.text = re.sub('】','',self.text,1)
self.damage = False
self.maeji = '】'
return self.tags.tagdict['suppliede']
#文脈による補釈(囲い文字)
m = re.search('^(.)&',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
value = tools.suppliedillegchar(m.group(1))
self.maeji = m.group(0)
return value
#句読点のエンコーディング
m = re.search('^([。,、;:?!])',self.text)
if m:
self.text = re.sub(m.group(0),'',self.text,1)
self.maeji = m.group(0)
return self.tags.tagdict['suppliedregb'] + m.group(1) + self.tags.tagdict['suppliede']
#字(外字の場合は"{A+B}"等)
m = re.search('^({[^}]+}|.)',self.text)
if m :
firstchar = m.group(1)
self.text = re.sub(m.group(1),'', self.text,1)
self.maeji = firstchar
return (firstchar)
#エディション比較においては、"self.dic.lookupglyph(firstchar)"の形で釈文の全ての漢字もglyphとしてエンコーディングしていく。
def textElementConvertion(self):
"""
釈文を文字単位に区切ってタグ・エレメントに変換する。
簡番号は、""エレメントにIDとして書き込まれ、変換済みの釈文の冒頭に置かれる。
"""
#簡番号のエンコーディング
self.convertedText = re.sub('簡番号',self.bango[-1],self.tags.tagdict['lb'])#タグエレメントに変換された簡番号で変換テキストの変数を初期化する
#文字単位が二行に跨る場合の処理(目前は"字=\n字=(字字,字字"のみ)
if self.zanyo:
self.text = self.zanyo + self.text
self.convertedText += Mojiretu.chopfirstchar(self)
print('二行に跨る処理のため手動による修正が必要\n{}'.format(self.convertedText))
self.zanyo = ''
#冒頭の文字列を切り取ってエンコーディングするchopfirstcharを、行末まで繰り返し呼び出す。
anzenshisu = 0
while self.text and not self.zanyo and anzenshisu<1000:
anzenshisu += 1
self.convertedText += Mojiretu.chopfirstchar(self)
return self.convertedText
class Head(Mojiretu):
"""
標題行を収めるオブジェクトを生成するクラス。
dicは、TEI-Encoder(shakudoku02)の冒頭で初期化された、漢字のglyphエンコーディング用の辞書を
オブジェクトとして引き渡す。
tagsはtags.pyにおいて生成されたタグの辞書を渡す。
"""
def __init__(self,head,dic,tags):
self.dic = dic
self.tags = tags
self.head = tags.tagdict['headb'] + head + tags.tagdict['heade'] + '\n' + self.tags.tagdict['pb']
class Honbun(Mojiretu):
"""
本文を収めるオブジェクトを生成するクラス。
dicは、TEI-Encoder(shakudoku02)の冒頭で初期化された、漢字のglyphエンコーディング用の辞書を
オブジェクトとして引き渡す。
tagsはtags.pyにおいて生成されたタグの辞書を渡す。
"""
def __init__(self,dic,tags):
self.no = -1
self.bango = []
self.honbun = []
self.dic = dic
self.tags = tags
self.zanyo = ''#文字列単位が二行に跨る場合に、一行目の末尾の文字を、次の行頭の処理に渡すために格納する。
self.damage = False#残欠の補釈のエンコーディングが複数の文字列単位に跨る場合に備えて、そうしたエンコーディングに入った時に"True"に変更する
self.omission = False#脱文のエンコーディングに入った時に"True"に変更し、エンコーディング完了後"False"に戻す
def addline(self,bango,text):
"""
一行分の釈文と簡番号を受け取り、釈文を文字単位に区切ってタグ・エレメントに変換する。
文字単位は、"字(字)"等の読み換えを示す文字列も一つの文字単位として扱う。
簡番号は、""エレメントにIDとして書き込まれ、変換済みの釈文の冒頭に置かれる。
"""
self.no += 1
self.bango.append(bango)
self.text = text
self.maeji = ''#"┘"が行頭に置かれているか、もしくはその前に句読点が置かれているかを確認するために用いる
self.honbun.append(Mojiretu.textElementConvertion(self) + '\n')
return
class Glyph:
"""
主として漢字もglyphとしてエンコーディングするための辞書。
現在予め原文の句読記号を現わす"●"・"┘"と重文記号を収めておいた。
解釈的釈文に見える現代の句読も予め入れておけば、そのエンコーディングも漢字と一括してできる。
(つまり、現在のやり方を改め、最初は原文句読記号や現代の句読点を全て単に文字として処理し、
文字エンコーディングにおいて一括して処理すればよいのかもしれない)
"""
glyphcount = -1
def __init__(self):
self.glyphdict = {
'●':'',
'┘':'',
'﹦':''}
def lookupglyph(self,char):
"""
字単位のエンコーディングを行う際に、辞書への収録を確認し、必要に応じて
辞書登録を行いつつ、glyphコードを返す。
原文句読記号や現代の句読点を除いては、0から始まる通し番号を附してエンコーディングを行う。
Parameters
----------
char : str
漢字などの文字。一字のみ
Returns
-------
str
glyphコード()
"""
if char in self.glyphdict.keys() :
pass
else:
type(self).glyphcount += 1
self.glyphdict[char] = re.sub('文字番号',str(type(self).glyphcount),self.tags.tagdict['glyphelement'])
return self.glyphdict[char]