#!/usr/bin/env python
"""
A python script to modify bib data and
to display references with specific bibliography standard
two key features:
两大核心功能
1. bib文件抽取,bib文件内容的自定义修改
2. 格式化文献表输出,包括json,bib,text,html,bbl文件,其中bbl文件可以在tex源代码中直接使用,利用natbib宏包可以实现不同的标注样式。
"""
__author__ = "Hu zhenzhen"
__version__ = "1.0"
__license__ = "MIT"
__email__ = "hzzmail@163.com"
import string
import re
import sys
import datetime
import copy
import json
import operator
#
# 为数据map设置参数
# 选项的逻辑与biblatex基本一致,
# 差别包括:overwrite选项可以放到step步中表示
#[]=maps
#[[],[]]=map in maps
#[[[]],[]]=step in map in mpas
#[[[{optionkey: }]],[]]=step in map in mpas
#注意python正则表达方式与perl的略有不同,比如unicode表示
#python用\xHH,\uHHHH,\UHHHHHHHH表示,而perl直接用\x{HHHH}表示。
sourcemaps=[#maps
[#map1:将ELECTRONIC类型转换为online类型
[{"typesource":"ELECTRONIC","typetarget":"online"}]#step1
],
[#map2:将source域转换为url域
[{"fieldsource":"source","fieldtarget":"url"}]#step1
],
[#map3:将urldate域的信息“yyyy-m-d”转换为“yyyy-mm-dd”,注意正则表达式直接写不用在外面套""
[{"fieldsource":"urldate","match":r'(\d\d\d\d)\-(\d)\-(\d)',"replace":r'\1-0\2-0\3'}]#step1
],
[#map4:将urldate域的信息“yyyy-m-d”转换为“yyyy-mm-dd”,注意正则表达式直接写不用在外面套""
[{"fieldsource":"date","match":r'(\d\d\d\d)\-(\d)\-(\d)',"replace":r'\1-0\2-0\3',"overwrite":True}]#step1
],
[#map5:将refdate域转换为urldate域
[{"fieldsource":"refdate","fieldtarget":"urldate"}]#step1
],
[#map6:对于newspaper类型,设置note为news
[{"pertype":"newspaper"}],#step1
[{"fieldset":"note","fieldvalue":"news","overwrite":True}]#step2
],
[#map7:设置edition域等于version
[{"fieldsource":"version","final":True}],#step1
[{"fieldset":"edition","origfieldval":True}]#step2
],
[#map8:设置entrykey域设置给keywords
[{"fieldsource":"entrykey"}],#step1
[{"fieldset":"keywords","origfieldval":True}]#step2
],
[#map9:对于存在note域的情况,将其值添加到keywords
[{"fieldsource":"note","final":True}],#step1
[{"fieldset":"keywords","origfieldval":True,"overwrite":True,"append":True}]#step2
],
[#map10:根据标题的字符编码范围确定标题的语言类型
[{"fieldsource":"title","match":r'[\u2FF0-\u9FA5]',"final":True}],#step1
[{"fieldset":"userd","fieldvalue":"chinese"}]#step2
],
]
#重设新的任务处理
sourcemaps=[
[#map1:设置entrykey域设置给keywords
[{"fieldsource":"entrykey"}],#step1
[{"fieldset":"keywords","origfieldval":True,"overwrite":True}]#step2
],
]
#重设新的任务
sourcemaps=[
[#map1:取消note域
[{"fieldsource":"note","final":True}],#step1
[{"fieldset":"note","null":True,"overwrite":True}]#step2
],
[#map2:取消abstract域
[{"fieldsource":"abstract","final":True}],#step1
[{"fieldset":"abstract","null":True,"overwrite":True}]#step2
],
[#map3:取消keywords域
[{"fieldsource":"keywords","final":True}],#step1
[{"fieldset":"keywords","null":True,"overwrite":True}]#step2
],
[#map4:取消keywords-plus域
[{"fieldsource":"keywords-plus","final":True}],#step1
[{"fieldset":"keywords-plus","null":True,"overwrite":True}]#step2
],
[#map5:取消affiliation域
[{"fieldsource":"affiliation","final":True}],#step1
[{"fieldset":"affiliation","null":True,"overwrite":True}]#step2
],
[#map6:取消funding-acknowledgement域
[{"fieldsource":"funding-acknowledgement","final":True}],#step1
[{"fieldset":"funding-acknowledgement","null":True,"overwrite":True}]#step2
],
[#map7:取消funding-text域
[{"fieldsource":"funding-text","final":True}],#step1
[{"fieldset":"funding-text","null":True,"overwrite":True}]#step2
],
]
#重设不处理
sourcemaps=[]
##------------------------------------------
## 参考文献表的制定格式设置,比如GB/T 7714-2015
##
#全局选项
formatoptions={
"style":'authoryear',#写bbl信息的设置选项,authoryear,'numeric'
"nameformat":'uppercase',#姓名处理选项:uppercase,lowercase,given-family,family-given,pinyin
"giveninits":'space',#使用名的缩写,space表示名见用空格分隔,dotspace用点加空格,dot用点,terse无分隔,false不使用缩写
"maxbibnames":3,#
"minbibnames":3,#
"morenames":True,#
"maxbibitems":1,#
"minbibitems":1,#
"moreitems":False,#
"date":'year',#'日期处理选项':year,iso,等
"urldate":'iso',#'日期处理选项':year,iso,等
}
#本地化字符串
localstrings={
'andothers':{'english':'et al.','chinese':'等'},
'and':{'english':' and ','chinese':'和'},
'edition':{'english':'th ed','chinese':'版'},#th ed. 中的点不要,为方便标点处理
'in':{'english':'in: ','chinese':'见: '},
'nolocation':{'english':'[S.l.]','chinese':'[出版地不详]'},
'nopublisher':{'english':'[s.n.]','chinese':'[出版者不详]'},
'bytranslator':{'english':'trans by','chinese':'译'},
}
#标点
localpuncts={
'multinamedelim':', ',
'finalnamedelim':', ',
'andothorsdelim':', ',
'finalitemdelim':', ',
'multiitemdelim':', ',
}
#替换字符串
replacestrings={
'[出版地不详]: [出版者不详]':'[出版地不详 : 出版者不详]',
'[S.l.]: [s.n.]':'[S.l. : s.n.]',
'..':'.',
}
#类型和载体字符串
typestrings={
'book':'[M]',
'inbook':'[M]',
'standard':'[S]',
'periodical':'[J]',
'article':'[J]',
'newspaper':'[N]',
'patent':'[P]',
'online':'[EB]',
'www':'[EB]',
'electronic':'[EB]',
'proceedings':'[C]',
'inproceedings':'[C]',
'conference':'[C]',
'collection':'[G]',
'incollection':'[G]',
'thesis':'[D]',
'mastersthsis':'[D]',
'phdthesis':'[D]',
'report':'[R]',
'techreport':'[R]',
'manual':'[A]',
'archive/manual':'[A]',
'database':'[DB]',
'dataset':'[DS]',
'software':'[CP]',
'map':'[CM]',
'unpublished':'[Z]',
'misc':'[Z]',
}
#数据类型
datatypeinfo={
'namelist':['author','editor','translator','bookauthor'],
'literallist':['location','address','publisher','institution','organization','school','language','keywords'],
'literalfield':['title','journaltitle','journal','booktitle','subtitle','titleaddon','edition','version','url',
'volume','number','endvolume','endnumber','type','note','labelnumber'],
'datefield':['date','year','urldate','enddate','origdate','eventdate'],
'rangefield':['pages']
}
#条目的著录格式
bibliographystyle={
"book":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['translator'],'options':{'nameformat':'uppercase'},'prepunct':". ",'posstring':r', \bibstring{bytranslator}'},
{"fieldsource":['edition'],'numerformat':'arabic','prepunct':". ","posstringifnumber":r'\bibstring{edition}'},
{"fieldsource":['location','address'],'prepunct':". ",'replstring':r"\bibstring{nolocation}"},
{"fieldsource":['publisher'],'prepunct':": ",'replstring':r"\bibstring{nopublisher}",'prepunctifnolastfield':'. '},
{"fieldsource":['date','year'],'prepunct':", "},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". ",'prestring':r'\url{','posstring':'}'},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"article":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['journaltitle','journal'],'prepunct':". "},
{"fieldsource":['year','date'],'prepunct':", "},
{"fieldsource":['volume'],'prepunct':", "},
{"fieldsource":['number'],'prestring':"(",'posstring':")"},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"newspaper":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['journaltitle','journal'],'prepunct':". "},
{"fieldsource":['date','year'],'prepunct':", ",'options':{'date':'iso'}},
{"fieldsource":['number'],'prestring':"(",'posstring':")"},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"inbook":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['in'],'replstring':"//",'omitifnofield':['bookauthor','editor','booktitle']},
{"fieldsource":['bookauthor','editor'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['booktitle'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':''},
{"fieldsource":['edition'],'numerformat':'arabic','prepunct':". ","posstring":r'\bibstring{edition}'},
{"fieldsource":['location','address'],'prepunct':". ",'replstring':r"\bibstring{nolocation}"},
{"fieldsource":['publisher'],'prepunct':": ",'replstring':r"\bibstring{nopublisher}",'prepunctifnolastfield':'. '},
{"fieldsource":['date','year'],'prepunct':", "},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"inproceedings":"inbook",
"incollection":"inbook",
"standard":"inbook",
"proceedings":"book",
"collection":"book",
"patent":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['number'],'prepunct':": "},
{"fieldsource":['date','year'],'prepunct':", "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"online":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['organization','instiution'],'prepunct':". "},
{"fieldsource":['date','year'],'prepunct':", ",'prepunctifnolastfield':'. ','omitifnofield':['enddate','eventdate']},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['date','enddate','eventdate'],'prepunct':". ",'options':{"date":"iso",'eventdate':'iso'},'prestring':"(","posstring":")"},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]",'prepunctifnolastfield':'. '},
{"fieldsource":['url'],'prepunct':". ",'prepunctifnolastfield':''},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"www":"online",
"electronic":"online",
"report":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['translator'],'options':{'nameformat':'uppercase'},'prepunct':". "},
{"fieldsource":['type'],'prepunct':". "},
{"fieldsource":['number'],'prepunct':"",'prepunctifnolastfield':'. '},
{"fieldsource":['version'],'prepunct':". "},
{"fieldsource":['location','address'],'prepunct':". ",'replstring':r"\bibstring{nolocation}"},
{"fieldsource":['institution','publisher'],'prepunct':": ",'replstring':r"\bibstring{nopublisher}",'prepunctifnolastfield':'. '},
{"fieldsource":['date','year'],'prepunct':", ",'omitifnofield':['location','address','institution','publisher'],'omitiffield':['url']},
{"fieldsource":['date','year'],'prepunct':". ",'prestring':'(','posstring':')','omitiffield':['location','address','institution','publisher']},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"techreport":"report",
"periodical":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['editor','author'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['year','date'],'prepunct':", "},
{"fieldsource":['volume'],'prepunct':", "},
{"fieldsource":['number'],'prestring':"(",'posstring':")-"},
{"fieldsource":['endyear','enddate'],'prepunct':", "},
{"fieldsource":['endvolume'],'prepunct':", "},
{"fieldsource":['endnumber'],'prestring':"(",'posstring':")"},
{"fieldsource":['location','address'],'prepunct':". ",'replstring':r"\bibstring{nolocation}"},
{"fieldsource":['institution','publisher'],'prepunct':": ",'replstring':r"\bibstring{nopublisher}",'prepunctifnolastfield':'. '},
{"fieldsource":['year','date'],'prepunct':", ",'posstring':'-'},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
#omitifnofield:必须所有的域都不存在才为true
#omitiffield:只要存在一个域就为true
"thesis":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['translator'],'options':{'nameformat':'uppercase'},'prepunct':". "},
{"fieldsource":['location','address'],'prepunct':". "},
{"fieldsource":['institution','publisher','school'],'prepunct':": ",'prepunctifnolastfield':'. '},
{"fieldsource":['date','year'],'prepunct':", ",'omitifnofield':['location','address','institution','publisher'],'omitiffield':['url']},
{"fieldsource":['date','year'],'prepunct':". ",'prestring':'(','posstring':')','omitiffield':['location','address','institution','publisher']},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". ",'prestring':r'\url{','posstring':'}'},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"manual":"thesis",
"unpublished":"thesis",
"database":"thesis",
"dataset":"thesis",
"software":"thesis",
"map":"thesis",
"archive":"thesis",
"misc":[
#{"fieldsource":["labelnumber"],'prestring':"[","posstring":"]","pospunct":" "},
{"fieldsource":['author','editor','translator'],'options':{'nameformat':'uppercase'}},
{"fieldsource":['title'],'caseformat':'sentencecase','prepunct':". ",'prepunctifnolastfield':'','posstring':r"\typestring"},
{"fieldsource":['howpublished'],'prepunct':". "},
{"fieldsource":['location','address'],'prepunct':". "},
{"fieldsource":['institution','publisher'],'prepunct':": ",'prepunctifnolastfield':'. '},
{"fieldsource":['date','year'],'prepunct':", "},
{"fieldsource":['pages'],'prepunct':": "},
{"fieldsource":['urldate'],'prestring':"[","posstring":"]"},
{"fieldsource":['url'],'prepunct':". "},
{"fieldsource":['doi'],'prepunct':". "},
{"fieldsource":['endpunct'],'replstring':"."}
],
"phdthesis":"thesis",
"mastersthesis":"thesis",
}
#
#
#打印格式化后的全部文献条目文本
def printbibliography():
bibliographyentries=bibliographytext.split('\n')
#md文件输出,直接用write写
mdoutfile="newformatted"+inputbibfile.replace('.bib','.md')
fout = open(mdoutfile, 'w', encoding="utf8")
print("INFO: writing cited references to '" + mdoutfile + "'")
biblabelnumber=0
for prtbibentry in bibliographyentries:
if len(prtbibentry)>0:
biblabelnumber=biblabelnumber+1
fout.write('['+str(biblabelnumber)+'] '+prtbibentry+'\n')
fout.close()
#html文件输出,直接用write写
mdoutfile="newformatted"+inputbibfile.replace('.bib','.html')
fout = open(mdoutfile, 'w', encoding="utf8")
print("INFO: writing cited references to '" + mdoutfile + "'")
fout.write('
references')
fout.write('')
fout.write('')
fout.write('')
fout.write(' |
')
biblabelnumber=0
for prtbibentry in bibliographyentries:
if len(prtbibentry)>0:
biblabelnumber=biblabelnumber+1
fout.write(' ')
fout.write('')
fout.write('['+str(biblabelnumber)+'] '+prtbibentry)
fout.write(' |
')
fout.write(' |
')
fout.write('
')
fout.close()
if auxfile:
bblfile=auxfile.replace('.aux','.bbl')
else:
bblfile=inputbibfile.replace('.bib','.bbl')
bbloutfile=bblfile
fout = open(bbloutfile, 'w', encoding="utf8")
print("INFO: writing cited references to '" + bbloutfile + "'")
fout.write(r'\begin{thebibliography}{'+str(len(bibentries))+'}\n')
biblabelnumber=0
for prtbibentry in bibliographyentries:
if len(prtbibentry)>0:
biblabelnumber=biblabelnumber+1
entrysn=0
for bibentry in bibentries:
entrysn=entrysn+1
if entrysn==biblabelnumber:
entrykeystr=bibentry['entrykey']
entryciteauthor=formatlabelauthor(bibentry)
entryciteyear=formatlabelyear(bibentry)
entrycitelabel=entryciteauthor[0]+'('+entryciteyear+')'+entryciteauthor[1]
#Baker et~al.(1995)Baker and Jackson
break
if formatoptions['style']=='authoryear':
fout.write(r'\bibitem['+entrycitelabel+']{'+entrykeystr+'}'+prtbibentry+'\n')
else:
fout.write(r'\bibitem['+str(biblabelnumber)+']{'+entrykeystr+'}'+prtbibentry+'\n')
fout.write(r'\end{thebibliography}')
fout.close()
#
#authoryear样式提供标注标签的作者信息
#
def formatlabelauthor(bibentry):
if 'author' in bibentry:
namelist=bibentry['author']
elif 'editor' in bibentry:
namelist=bibentry['editor']
elif 'translator' in bibentry:
namelist=bibentry['translator']
else:
namelist='Anon'
return [namelist,namelist]
#
#authoryear样式提供标注标签的年份信息
#
def formatlabelyear(bibentry):
if 'year' in bibentry:
yearlist=bibentry['year']
elif 'eventyear' in bibentry:
yearlist=bibentry['eventyear']
elif 'origyear' in bibentry:
yearlist=bibentry['origyear']
elif 'urlyear' in bibentry:
yearlist=bibentry['urlyear']
else:
yearlist='N.D.'
return yearlist
#
#
#格式化全部文献条目文本
def formatallbibliography():
labelnumber=0
global bibliographytext
bibliographytext=''
for bibentry in bibentries:
labelnumber=labelnumber+1
bibentry['labelnumber']=labelnumber
bibentrytext=''
bibentrytext=formatbibentry(bibentry)
bibliographytext=bibliographytext+bibentrytext+'\n'
print('\nreferecences')
print(bibliographytext)
#
#
#格式化一个文献条目文本
def formatbibentry(bibentry):
print('--------------new entry---------')
print('\nbibentry:',bibentry)
#bibentrytext='entry:'
#
# 由于volume和number域可能存在范围的特殊情况,首先做特殊处理
#
if 'volume' in bibentry:
if '-' in bibentry['volume']:
multivolume=bibentry['volume'].split("-")
bibentry['volume']=multivolume[0]
bibentry['endvolume']=multivolume[1]
if 'number' in bibentry:
if '-' in bibentry['number']:
multinumber=bibentry['number'].split("-")
bibentry['number']=multinumber[0]
bibentry['endnumber']=multinumber[1]
#四种日期域也做范围解析
if 'date' in bibentry:
if '/' in bibentry['date']:
datestring=bibentry['date'].split('/')
bibentry['date']=datestring[0]
bibentry['enddate']=datestring[1]
if 'urldate' in bibentry:
if '/' in bibentry['urldate']:
datestring=bibentry['urldate'].split('/')
bibentry['urldate']=datestring[0]
bibentry['endurldate']=datestring[1]
if 'eventdate' in bibentry:
if '/' in bibentry['eventdate']:
datestring=bibentry['eventdate'].split('/')
bibentry['eventdate']=datestring[0]
bibentry['endeventdate']=datestring[1]
if 'origdate' in bibentry:
if '/' in bibentry['origdate']:
datestring=bibentry['origdate'].split('/')
bibentry['origdate']=datestring[0]
bibentry['endorigdate']=datestring[1]
#
#接着处理所有域到一个条目文本
#
bibentrytext=''
if bibentry['entrytype'] in bibliographystyle:
print('INFO: format style of entrytype',bibentry['entrytype'],'is defined.')
if isinstance(bibliographystyle[bibentry['entrytype']],str):
formattype=bibliographystyle[bibentry['entrytype']]
else:
formattype=bibentry['entrytype']
lastfield=True #前一域存在
for fieldinfo in bibliographystyle[formattype]:
rtnfield=formatfield(bibentry,fieldinfo,lastfield)
fieldtext=rtnfield[0]
lastfield=rtnfield[1]
bibentrytext=bibentrytext+fieldtext
#对替换字符串做处理
#包括对重复的标点做处理比如:..变为.
for k,v in replacestrings.items():
print(k,v)
#m=re.search(k,bibentrytext)
#print(m)
#bibentrytext=re.sub(k,v,bibentrytext)
#利用正则反而不行,直接用字符串替换
bibentrytext=bibentrytext.replace(k,v)
print(bibentrytext)
return bibentrytext
#
#
#格式化文献条目的域
#不同类型的域不同处理
#分5类:姓名列表,文本列表,文本域,日期域,范围域
#其中姓名列表,文本列表,日期域,日期域,范围域,都需要进行特殊的解析
#而volume,number如果需要特殊解析则在文件域的格式处理时增加新的处理逻辑。
def formatfield(bibentry,fieldinfo,lastfield):
print('fieldinfo:',fieldinfo)
#首先把域的内容先解析处理
fieldcontents=''
fieldsource=None
#首先判断域是否忽略
fieldomit=False
if 'omitifnofield' in fieldinfo and 'omitiffield' in fieldinfo:
fieldomita=True#假设忽略的条件满足
for field in fieldinfo['omitifnofield']:#只要需要不存在的域有一个存在,那么条件就不满足
if field in bibentry:
fieldomita=False
break
fieldomitb=False#假设忽略的条件不满足
for field in fieldinfo['omitiffield']:#只要需要存在的域中有一个存在,那么条件就满足
if field not in bibentry:
fieldomitb=True
break
fieldomit=fieldomita and fieldomitb
elif 'omitifnofield' in fieldinfo:
fieldomit=True#假设忽略的条件满足
for field in fieldinfo['omitifnofield']:#只要需要不存在的域有一个存在,那么条件就不满足
if field in bibentry:
fieldomit=False
break
elif 'omitiffield' in fieldinfo:
fieldomit=False#假设忽略的条件不满足
for field in fieldinfo['omitiffield']:#只要需要存在的域中有一个存在,那么条件就满足
if field not in bibentry:
fieldomit=True
break
print(fieldomit)
#如果不忽略该域那么:
if not fieldomit:
#当域为姓名列表域时:
if fieldinfo['fieldsource'][0] in datatypeinfo['namelist']:
#print('0',fieldinfo['fieldsource'][0])
#print('author' in bibentry)
for field in fieldinfo['fieldsource']:#
#print(fieldinfo['fieldsource'])
#print('namelist:',field)
if field in bibentry:#当域存在域条目中时,确定要处理的域
fieldsource=field
break
if fieldsource:
#传递条目给出的一些控制选项
if 'options' in fieldinfo:
options=fieldinfo['options']
else:
options={}
fieldcontents=namelistparser(bibentry,fieldsource,options)
#当域为文本列表域时:
elif fieldinfo['fieldsource'][0] in datatypeinfo['literallist']:
for field in fieldinfo['fieldsource']:#
if field in bibentry:#当域存在域条目中时,确定要处理的域
fieldsource=field
break
if fieldsource:
fieldcontents=literallistparser(bibentry,fieldsource)
#当域为文本域时:
elif fieldinfo['fieldsource'][0] in datatypeinfo['literalfield']:
for field in fieldinfo['fieldsource']:#
if field in bibentry:#当域存在域条目中时,确定要处理的域
fieldsource=field
break
if fieldsource:
fieldcontents=literalfieldparser(bibentry,fieldsource)
#当域为日期域时:
elif fieldinfo['fieldsource'][0] in datatypeinfo['datefield']:
for field in fieldinfo['fieldsource']:#
if field in bibentry:#当域存在域条目中时,确定要处理的域
fieldsource=field
break
if fieldsource:
#传递条目给出的一些控制选项
if 'options' in fieldinfo:
options=fieldinfo['options']
else:
options={}
fieldcontents=datefieldparser(bibentry,fieldsource,options)
#当域为范围域时:
elif fieldinfo['fieldsource'][0] in datatypeinfo['rangefield']:
for field in fieldinfo['fieldsource']:#
if field in bibentry:#当域存在域条目中时,确定要处理的域
fieldsource=field
break
if fieldsource:
fieldcontents=rangefieldparser(bibentry,fieldsource)
if not fieldsource:
if 'replstring' in fieldinfo and fieldinfo['replstring']:
print('replstring')
fieldsource=True
fieldcontents=fieldinfo['replstring']
#接着做进一步的格式化,包括标点,格式,字体等
fieldtext=''
print(fieldsource)
if fieldsource:
if lastfield:#当前一个著录项存在,则正常输出
if 'prepunct' in fieldinfo:
fieldtext=fieldtext+fieldinfo['prepunct']
else:#当前一个著录项不存在,则首先输出'prepunctifnolastfield'
if 'prepunctifnolastfield' in fieldinfo:
fieldtext=fieldtext+fieldinfo['prepunctifnolastfield']
elif 'prepunct' in fieldinfo:
fieldtext=fieldtext+fieldinfo['prepunct']
if 'prestring' in fieldinfo:
fieldtext=fieldtext+fieldinfo['prestring']
if 'fieldformat' in fieldinfo:
fieldtext=fieldtext+'{'+fieldinfo['fieldformat']+'{'+fieldcontents+'}}'
else:
fieldtext=fieldtext+str(fieldcontents)
if 'posstring' in fieldinfo:
fieldtext=fieldtext+fieldinfo['posstring']
if 'posstringifnumber' in fieldinfo:
try:
numtemp=int(fieldcontents)
if isinstance(numtemp,int):
fieldtext=fieldtext+fieldinfo['posstringifnumber']
except:
print('info:waring the field value can not convert to integer')
if 'pospunct' in fieldinfo:
fieldtext=fieldtext+fieldinfo['pospunct']
#更新lastfiled
lastfield=True
else:
lastfield=False
#自定义标点的处理
print('fieldtext:',fieldtext)
while r'\printdelim' in fieldtext:
m = re.search(r'\\printdelim{([^\}]*)}',fieldtext)#注意贪婪算法的影响,所以要排除\}字符
print('m.group(1):',m.group(1))
fieldtext=re.sub(r'\\printdelim{[^\}]*}',localpuncts[m.group(1)],fieldtext,count=1)
print('fieldtext:',fieldtext)
#本地化字符串的处理
print('fieldtext:',fieldtext)
while r'\bibstring' in fieldtext:
language=languagejudgement(bibentry,fieldinfo,fieldsource)
m = re.search(r'\\bibstring{([^\}]*)}',fieldtext)#注意\字符的匹配,即便是在r''中也需要用\\表示
fieldtext=re.sub(r'\\bibstring{[^\}]*}',localstrings[m.group(1)][language],fieldtext,count=1)
print('fieldtext:',fieldtext)
#下面这句不行因为,在字典取值是,不支持\1这样的正则表达式
#fieldtext=re.sub(r'\\bibstring{(.*)}',localstrings[r'\1'][language],fieldtext,count=1)
#标题的类型和载体标识符的处理
if r'\typestring' in fieldtext:#当需要处理类型和载体时
if bibentry['entrytype'] in typestrings:#当条目对应的类型存在时
print(r'\typestring in',fieldtext)
typestring=typestrings[bibentry['entrytype']]
if 'url' in bibentry:
typestring=typestring.replace(']','/OL]')
elif 'medium' in bibentry:
rplctypestring=bibentry['medium']+']'
typestring=typestring.replace(']',rplctypestring)
else:#当条目对应的类型不存在时,当做其它类型处理
typestring='[Z]'
print(typestring)
fieldtext=fieldtext.replace(r'\typestring',typestring)
return [fieldtext,lastfield]
#
#根据作者域或者标题域确定条目的语言
#
def languagejudgement(bibentry,fieldinfo,fieldsource):
if fieldsource in datatypeinfo['namelist']:#当域是作者类时,利用作者域本身信息做判断
language=fieldlanguage(bibentry[fieldsource])
else:#其它情况,利用title域做判断
if 'title' in bibentry:
language=fieldlanguage(bibentry['title'])
elif 'author' in bibentry:
language=fieldlanguage(bibentry['author'])
else:
language='english'
return language
#
#根据域值所在的字符范围确定域的语言
#
def fieldlanguage(fieldvalueinfo):
if re.match(r'[\u2FF0-\u9FA5]', fieldvalueinfo):
language='chinese'
elif re.match(r'[\u3040-\u30FF\u31F0}-\u31FF]', fieldvalueinfo):
language='japanese'
elif re.match(r'[\u1100-\u11FF\u3130-\u318F\uAC00-\uD7AF]', fieldvalueinfo):
language='korean'
elif re.match(r'[\u0400-\u052F]', fieldvalueinfo):
language='russian'
elif re.match(r'[\u0100-\u017F]', fieldvalueinfo):
language='french'
else:
language='english'
return language
#
#
#对存在{}做保护的字符串进行分割
def safetysplit(strtosplt,seps):
#首先查找{}保护的所有字符串
s1=re.findall('\{.*?\}',strtosplt)
#接着确定保护字符串中是否存在分割用字符串
sepinbrace=False
for s1a in s1:
for sep in seps:
if sep in s1a:
sepinbrace=True
break
if sepinbrace:
break
#print(sepinbrace)
#若保护字符串中存在分割字符串那么做特殊处理
a=strtosplt
if sepinbrace:
#保护字符串用特殊字符串代替,特殊字符串与分割字符串没有任何关联
strsn=0
for stra1 in s1:
strsn=strsn+1
a=a.replace(stra1,'$'+str(strsn)+'$')
#print(a)
#处理原字符串为只需要一个分隔字符串就能分隔
if len(seps)>1:
for i in range(1,len(seps)):
a=a.replace(seps[i],seps[0])
#print(a)
#接着做分割
names=a.split(seps[0])
#print(names)
#对分割后的字符串做还原,即把特殊字符串还原回保护字符串
namesnew=[]
for name in names:
strsn=0
for stra1 in s1:
strsn=strsn+1
name=name.replace('$'+str(strsn)+'$',stra1)
#print(name)
namesnew.append(name.strip().lstrip())
else:
#处理原字符串为只需要一个分隔字符串就能分隔
if len(seps)>1:
for i in range(1,len(seps)):
a=a.replace(seps[i],seps[0])
#print(a)
#直接分割
names=a.split(seps[0])
namesnew=[]
for name in names:
namesnew.append(name.strip().lstrip())
#print(namesnew)
return namesnew
#
#
#姓名列表解析
#增加条目给出的选项
def namelistparser(bibentry,fieldsource,options):
fieldcontents=bibentry[fieldsource]
#首先做针对{}保护的处理
#{}有可能保护一部分,有可能保护全部
#首先判断{}是否存在,若存在,那么可以确定需要做保护处理,否则用常规处理
#当然这些事情可以在一个函数中处理
#首先姓名列表进行分解,包括用' and '和' AND '做分解
#利用safetysplit函数实现安全的分解
seps=[' and ',' AND ']
fieldcontents=fieldcontents.lstrip().strip()
fieldauthors=safetysplit(fieldcontents,seps)
print('fieldauthors:',fieldauthors)
#接着从各个姓名得到更详细的分解信息
fieldnames=[]
for name in fieldauthors:
if name.lower() == 'others':
nameinfo={'morename':True}
else:
if fieldlanguage(name)=='chinese':#当中文姓名中存在逗号先去除
if ', ' in name:
name=name.replace(', ','')
else:
pass
nameinfo=singlenameparser(name)
fieldnames.append(nameinfo)
#最后根据全局和局部选项进行格式化
nameformattedstr=''
#根据'maxbibnames'和'minbibnames'截短
if 'maxbibnames' in options:#首先使用条目中的选项
if len(fieldnames)>options['maxbibnames']:
fieldnamestrunc=fieldnames[:options['minbibnames']]
nameinfo={'morename':True}
fieldnamestrunc.append(nameinfo)
else:
fieldnamestrunc=fieldnames
elif 'maxbibnames' in formatoptions:#接着使用全局选项
if len(fieldnames)>formatoptions['maxbibnames']:
fieldnamestrunc=fieldnames[:formatoptions['minbibnames']]
nameinfo={'morename':True}
fieldnamestrunc.append(nameinfo)
else:
fieldnamestrunc=fieldnames
else:
fieldnamestrunc=fieldnames
#当条目选择中存在'nameformat'
if 'nameformat' in options:
option={'nameformat':options['nameformat']}
else:
option={}
print('fieldnamestrunc:',fieldnamestrunc)
nameliststop=len(fieldnamestrunc)
nameliststart=1
namelistcount=0
for nameinfo in fieldnamestrunc:
namelistcount=namelistcount+1
if 'morename' in nameinfo:
if formatoptions['morenames']:#只有设置morenames为true是才输出other的相关信息
nameformattedstr=nameformattedstr+r'\printdelim{andothorsdelim}\bibstring{andothers}'
else:
if namelistcount==nameliststop and namelistcount>1:#当没有others时最后一个姓名前加的标点
nameformattedstr=nameformattedstr+r'\printdelim{finalnamedelim}'+singlenameformat(nameinfo,option)
elif namelistcount==nameliststart:
nameformattedstr=singlenameformat(nameinfo,option)
else:
nameformattedstr=nameformattedstr+r'\printdelim{multinamedelim}'+singlenameformat(nameinfo,option)
return nameformattedstr
#
#
#单个姓名格式化
def singlenameformat(nameinfo,option):
singlenamefmtstr=''
if 'nameformat' in option:#首先使用条目给出的选项
nameformat=option['nameformat']
elif 'nameformat' in formatoptions:#接着使用全局的选项
nameformat=formatoptions['nameformat']
else:#最后使用默认的选项
nameformat='uppercase'
#根据单个姓名格式化选项来实现具体的格式
if nameformat=='uppercase':
if nameinfo['family'].startswith('{'):
singlenamefmtstr=nameinfo['family']
else:
singlenamefmtstr=nameinfo['family'].upper()
#根据选项确定使用名的缩写
if formatoptions["giveninits"]=='space':#space表示名见用空格分隔,
if 'given' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['giveni'].upper()
if 'middle' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['middlei'].upper()
elif formatoptions["giveninits"]=='dotspace':#dotspace用点加空格,
if 'given' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['giveni'].upper()
if 'middle' in nameinfo:
singlenamefmtstr=singlenamefmtstr+'. '+nameinfo['middlei'].upper()+'.'
elif formatoptions["giveninits"]=='dot':#dot用点,
if 'given' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['giveni'].upper()
if 'middle' in nameinfo:
singlenamefmtstr=singlenamefmtstr+'.'+nameinfo['middlei'].upper()+'.'
elif formatoptions["giveninits"]=='terse':#terse无分隔,
if 'given' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['giveni'].upper()
if 'middle' in nameinfo:
singlenamefmtstr=singlenamefmtstr+nameinfo['middlei'].upper()
elif formatoptions["giveninits"]=='false':#false不使用缩写
if 'given' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['given'].upper()
if 'middle' in nameinfo:
singlenamefmtstr=singlenamefmtstr+' '+nameinfo['middle'].upper()
elif nameformat=='lowercase':
pass
elif nameformat=='given-family':
pass
elif nameformat=='family-given':
pass
elif nameformat=='pinyin':
pass
else:
pass
return singlenamefmtstr
#
#
#单个姓名解析
def singlenameparser(name):
singlename=name
print('name:',name)
#字典用于存储所有的姓名成分信息
namepartsinfo={}
nameparts=safetysplit(singlename,[','])
if len(nameparts)==3:#两个逗号的情况,表示存在family和suffix
prefixfamily=safetysplit(nameparts[0].lstrip().strip(),[' '])
if len(prefixfamily)>1:
namepartsinfo['prefix']=prefixfamily[0].lstrip().strip()
namepartsinfo['family']=prefixfamily[1].lstrip().strip()
else:
namepartsinfo['family']=prefixfamily[0].lstrip().strip()
namepartsinfo['suffix']=nameparts[1].lstrip().strip()
givenmiddle=safetysplit(nameparts[2].lstrip().strip(),[' '])
if len(givenmiddle)>1:
namepartsinfo['given']=givenmiddle[0].lstrip().strip()
namepartsinfo['middle']=givenmiddle[1].lstrip().strip()
else:
namepartsinfo['given']=givenmiddle[0].lstrip().strip()
elif len(nameparts)==2:
prefixfamily=safetysplit(nameparts[0].lstrip().strip(),[' '])
if len(prefixfamily)>1:
namepartsinfo['prefix']=prefixfamily[0].lstrip().strip()
namepartsinfo['family']=prefixfamily[1].lstrip().strip()
else:
namepartsinfo['family']=prefixfamily[0].lstrip().strip()
givenmiddle=safetysplit(nameparts[1].lstrip().strip(),[' '])
if len(givenmiddle)>1:
namepartsinfo['given']=givenmiddle[0].lstrip().strip()
namepartsinfo['middle']=givenmiddle[1].lstrip().strip()
else:
namepartsinfo['given']=givenmiddle[0].lstrip().strip()
else:
givenmiddlefamily=safetysplit(nameparts[0].lstrip().strip(),[' '])
if len(givenmiddlefamily)==3:
namepartsinfo['given']=givenmiddlefamily[0].lstrip().strip()
namepartsinfo['middle']=givenmiddlefamily[1].lstrip().strip()
namepartsinfo['family']=givenmiddlefamily[2].lstrip().strip()
elif len(givenmiddlefamily)==2:
namepartsinfo['given']=givenmiddlefamily[0].lstrip().strip()
namepartsinfo['family']=givenmiddlefamily[1].lstrip().strip()
elif len(givenmiddlefamily)==1:
namepartsinfo['family']=givenmiddlefamily[0].lstrip().strip()
print('nameparts:',namepartsinfo)
if 'family' in namepartsinfo:
if namepartsinfo['family'].startswith('{'):
namepartsinfo['familyi']=namepartsinfo['family']
else:
namepartsinfo['familyi']=namepartsinfo['family'][0].upper()
if 'given' in namepartsinfo:
#print(namepartsinfo['given'])
if namepartsinfo['given'].startswith('{'):
namepartsinfo['giveni']=namepartsinfo['given']
else:
namepartsinfo['giveni']=namepartsinfo['given'][0].upper()
if 'middle' in namepartsinfo:
if namepartsinfo['middle'].startswith('{'):
namepartsinfo['middle']=namepartsinfo['middle']
else:
namepartsinfo['middlei']=namepartsinfo['middle'][0].upper()
return namepartsinfo
#
#
#文本列表解析
def literallistparser(bibentry,fieldsource):
fieldcontents=bibentry[fieldsource]
#首先从文本列表分解出各个项,包括用' and '和' AND '做分解
#利用safetysplit函数实现安全的分解
seps=[' and ',' AND ']
fieldcontents=fieldcontents.lstrip().strip()
fielditems=safetysplit(fieldcontents,seps)
print('fielditems:',fielditems)
#根据'maxbibitems'和'minbibitems'截短
fielditemstrunc=[]
if len(fielditems)>formatoptions['maxbibitems']:
fielditemstrunc=fielditems[:formatoptions['minbibitems']]
fielditemstrunc.append('others')
else:
fielditemstrunc=fielditems
#重设一下others的大小写,因为可能输入的时大写的
if fielditems[-1].lower() == 'others':
fielditems[-1]='others'
#最后根据全局和局部选项进行格式化
itemliststop=len(fielditemstrunc)
itemliststart=1
itemlistcount=0
itemformattedstr=''
for iteminfo in fielditemstrunc:
itemlistcount=itemlistcount+1
if iteminfo == 'others':
if formatoptions['moreitems']:#只有设置moreitems为true是才输出other的相关信息
itemformattedstr=itemformattedstr+r'\printdelim{andothorsdelim}\bibstring{andothers}'
else:
if itemlistcount==itemliststop and itemlistcount>1:#当没有others时最后一个项前加标点
itemformattedstr=itemformattedstr+r'\printdelim{finalitemdelim}'+iteminfo
elif itemlistcount==itemliststart:
itemformattedstr=iteminfo
else:
itemformattedstr=itemformattedstr+r'\printdelim{multiitemdelim}'+iteminfo
return itemformattedstr
#
#
#文本域解析
def literalfieldparser(bibentry,fieldsource):
fieldcontents=bibentry[fieldsource]
return fieldcontents
#
#
#日期域解析
#条目设置的选项options:
def datefieldparser(bibentry,fieldsource,options):
fieldcontents=bibentry[fieldsource]
#首先从日期域,解析日期类型:
dateparts={}
if fieldsource=='year':
datestring=bibentry[fieldsource]
datetype=''
else:
datetype=fieldsource.replace('date','')
datestring=bibentry[fieldsource]
#日期的年月日解析
#日期一般很少用{}进行保护,当保护的时候通常是整个进行包括,所以通常{}会出现在域的起始和末尾
#所以当出现{字符时通常不用再做解析
if '{' in datestring:
datepartinfo=[datestring]
else:
datepartinfo=datestring.split('-')
if len(datepartinfo)==3:
dateparts[datetype+'year']=datepartinfo[0].strip().lstrip()
dateparts[datetype+'month']=datepartinfo[1].strip().lstrip()
dateparts[datetype+'day']=datepartinfo[2].strip().lstrip()
elif len(datepartinfo)==2:
dateparts[datetype+'year']=datepartinfo[0].strip().lstrip()
dateparts[datetype+'month']=datepartinfo[1].strip().lstrip()
else:
dateparts[datetype+'year']=datepartinfo[0].strip().lstrip()
print(dateparts)
#判断解析的年月日是不是整数,若不是则表示日期不可解析
datecanbeparse=True
for k,v in dateparts.items():
try:
datepartisint=isinstance(int(v),int)
except:
print('INFO: WARNING'+fieldsource+'can not be parsed')
datecanbeparse=False
break
#然后根据全局选项进行格式化:
#如果解析的年月日不是整数,那么对于date域则忽略,对于year域则直接输出
if datecanbeparse:
if datetype+'date' in options:#首先使用条目给出的选项
fieldcontents=singledateformat(dateparts,datetype,options[datetype+'date'])
elif datetype+'date' in formatoptions:#接着使用全局给出的选项
fieldcontents=singledateformat(dateparts,datetype,formatoptions[datetype+'date'])
else:#否则使用默认选项
fieldcontents=singledateformat(dateparts,datetype,'default')
else:
if fieldsource=='year':
fieldcontents=bibentry[fieldsource]
else:
fieldcontents=''
return fieldcontents
#
#
# 根据日期的设置选项格式化单个日期
def singledateformat(dateparts,datetype,formatoption):
if formatoption=='year':
dateformatstr=dateparts[datetype+'year']
elif formatoption=='iso':
dateformatstr=dateparts[datetype+'year']
if len(dateparts[datetype+'month'])<2:
dateformatstr=dateformatstr+'-0'+dateparts[datetype+'month']
else:
dateformatstr=dateformatstr+'-'+dateparts[datetype+'month']
if len(dateparts[datetype+'day'])<2:
dateformatstr=dateformatstr+'-0'+dateparts[datetype+'day']
else:
dateformatstr=dateformatstr+'-'+dateparts[datetype+'day']
elif formatoption=='ymd':
dateformatstr=dateparts[datetype+'year']
dateformatstr=dateformatstr+'-'+dateparts[datetype+'month']
dateformatstr=dateformatstr+'-'+dateparts[datetype+'day']
elif formatoption=='default':
dateformatstr=dateparts[datetype+'year']
else:
pass
return dateformatstr
#
#
#范围域解析
def rangefieldparser(bibentry,fieldsource):
fieldcontents=bibentry[fieldsource]
return fieldcontents
#
#
#打开bib文件
def readfilecontents(bibFile):
print("INFO: Reading references from '" + bibFile + "'")
try:
fIn = open(bibFile, 'r', encoding="utf8")
global bibfilecontents
bibfilecontents=fIn.readlines()
fIn.close()
global usedIds
usedIds = set()
if auxfile:
fInAux = open(auxfile, 'r', encoding="utf8")
for line in fInAux:
if line.startswith("\\citation") or line.startswith("\\abx@aux@cite"):
ids = line.split("{")[1].rstrip("} \n").split(", ")
for id in ids:
if (id != ""):
usedIds.add(id)
fInAux.close()
print('references:',usedIds)
except IOError:
print("ERROR: Input bib file '" + bibFile +
"' doesn't exist or is not readable")
sys.exit(-1)
#
#打印bib文件内容
def printfilecontents():
for line in bibfilecontents:
print(line)
#
#输出修改后的bib文件
def writefilenewbib(bibFile):
print(datetime.datetime.now().isoformat(timespec='seconds'))
#json文件输出,用dump方法
jsonoutfile="new"+bibFile.replace('.bib','.json')
print("INFO: writing all references to '" + jsonoutfile + "'")
fout = open(jsonoutfile, 'w', encoding="utf8")
json.dump(bibentries, fout)
fout.close()
#json文件输出,直接用write写
jsonoutfile="new"+bibFile.replace('.bib','cited.json')
fout = open(jsonoutfile, 'w', encoding="utf8")
print("INFO: writing cited references to '" + jsonoutfile + "'")
fout.write('[\n')
for bibentry in bibentries:
if bibentry["entrykey"] in usedIds or not usedIds:
fout.write(repr(bibentry)+',\n')
fout.write(']\n')
fout.close()
#bib文件输出
biboutfile="new"+bibFile
try:
fout = open(biboutfile, 'w', encoding="utf8")
fout.write("%% \n")
fout.write("%% bib file modified by biblatex-map.py\n")
fout.write("%% "+datetime.datetime.now().isoformat(timespec='seconds')+"\n")
fout.write("%% \n\n\n")
for bibcomment in bibcomments:
for k,v in bibcomment.items():
if k=="entrytype":
fout.write('@'+bibcomment["entrytype"]+'{')
else:
fout.write(str(v))
fout.write('}\n\n')
for bibstring in bibstrings:
for k,v in bibstring.items():
if k=="entrytype":
fout.write('@'+bibstring["entrytype"]+'{')
else:
fout.write(str(v))
fout.write('}\n\n')
writebibentrycounter=0
if auxfile:
print("INFO: writing cited references in aux to '" + biboutfile + "'")
for bibentry in bibentries:
if bibentry["entrykey"] in usedIds:
writebibentrycounter=writebibentrycounter+1
fout.write('@'+bibentry["entrytype"]+'{'+bibentry["entrykey"]+',\n')
for k,v in bibentry.items():
if k=="entrytype" or k=="entrykey" or v=='""' or v==None or k=='entrysn':
pass
else:
fout.write('\t'+str(k)+' = {'+str(v)+'},\n')
fout.write('}\n\n')
else:
print("INFO: writing all references to '" + biboutfile + "'")
for bibentry in bibentries:
writebibentrycounter=writebibentrycounter+1
fout.write('@'+bibentry["entrytype"]+'{'+bibentry["entrykey"]+',\n')
for k,v in bibentry.items():
if k=="entrytype" or k=="entrykey" or v=='""' or k=='entrysn' or v==None:
pass
else:
fout.write('\t'+k+' = {'+v+'},\n')
fout.write('}\n\n')
fout.close()
print("INFO: " + str(writebibentrycounter) + " references writed")
except IOError:
print("ERROR: Input bib file '" + bibFile +
"' doesn't exist or is not readable")
sys.exit(-1)
#
#将条目解析放到bibentries列表中
#每个条目是一个dict字典
def bibentryparsing():
global bibentries
global bibcomments
global bibstrings
bibentries=[]#用于记录所有条目
bibentry={}#用于记录当前条目
bibcomments=[]#用于记录所有@comment条目
bibcomment={}#用于记录当前@comment条目
bibstrings=[]#用于记录所有@stirng条目
bibstring={}#用于记录当前@stirng条目
entrysn=0 #用于标记条目序号
bibentrycounter=0##用于标记条目总数
commentsn=0 #用于标记@comment条目序号
bibcommentcounter=0#用于标记@comment条目总数
stringsn=0 #用于标记@stirng条目序号
bibstringcounter=0#用于标记@stirng条目总数
entrystated=False #用于标记条目开始
fieldvalended=True #用于标记域的值当前行是否已经结束
fieldvalue="" #用于记录当前域的值
counterbracket=0 #用于追踪{}符号
counterquotes=0 #用于追踪“”符号
enclosebracket=True #用于记录是用{}还是用“”做为域的值的包围符号
enclosenone=False #用于记录域的值无包围符号的情况
for line in bibfilecontents:#遍历所有行
#print(line)
if line.startswith("@") and not "@comment" in line.lower() and not "@string" in line.lower():#判断条目开始行
entrysn=entrysn+1
entrystated=True #新条目开始
print('entrysn=',entrysn)
entrynow=line.lstrip('@').split(sep='{', maxsplit=1)
#print(entrynow)
entrytype=entrynow[0]
bibentry['entrytype']=entrytype.lower()#条目类型小写,方便比较
entrykey=entrynow[1].split(sep=',', maxsplit=1)[0]
bibentry['entrykey']=entrykey
bibentry['entrysn']=entrysn
elif entrystated: #只有新条目开始了才有意义
if fieldvalended: #当前行不是前面的未结束域的值
if '=' in line and not line.lstrip().startswith("}"):
#排除以'}'开头的行后根据=号判断条目域信息行,不可能出现=号无法判断信息行的问题,
#因为是域值中存在=的特殊情况已经在未结束逻辑处理
entryline=line.lstrip()
entrynow=entryline.split(sep='=', maxsplit=1)
#print(entrynow)
entryfield=entrynow[0].strip().lstrip().lower()#域名小写,方便比较
entryfieldline=entrynow[1].lstrip()
if entryfieldline.startswith("{"):
enclosebracket=True
elif entryfieldline.startswith('"'):
enclosebracket=False
else:#当没有符号包围是设置enclosenone以便特殊处理
enclosenone=True
fieldvalcontinued=True #临时标记,用于记录域值是否还未结束,先假设未结束
for chari in entryfieldline.strip():#遍历域值中的每个字符
fieldvalue=fieldvalue+chari
if chari =='{':#对{符号进行追踪
counterbracket=counterbracket+1
elif chari =='}':#对}符号进行追踪
counterbracket=counterbracket-1
if enclosebracket:
if counterbracket==0:#当}与域开始的{配对,那么说明域值已经结束
bibentry[entryfield]=fieldvalue[1:-1]#利用strip可能会消多次,因此用list的方式处理
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
break #直接跳出循环
elif chari =='"':
counterquotes=counterquotes+1
if not enclosebracket:
if operator.mod(counterquotes,2)==0:
bibentry[entryfield]=fieldvalue[1:-1]
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
break
elif chari ==',':#若域值没有包围符号那么遇到,号即为域值结束
if enclosenone:
bibentry[entryfield]=fieldvalue[:-1]
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
enclosenone=False
#测试:输出看是否正确
#if entryfield=='abstract':
# print('val=',fieldvalue)
# print('counterbracket=',counterbracket)
if fieldvalcontinued:
fieldvalended=False
elif '}' in line:#条目结束行
#global bibentryglobal
#bibentryglobal=copy.deepcopy(bibentry)
#print('entry:',bibentryglobal)
#bibentries.append(bibentryglobal)
print('entry:',bibentry)
bibentries.append(bibentry)
bibentry={}
entrystated=False
else: #当前行是前面的未结束域的值,因此直接往前面的域值添加即可
fieldvalcontinued=True
entryfieldline=line
if enclosenone:#当域没有包围符号时,接续的行可能是用逗号结束的域,也可能没有逗号,而用}直接结束条目信息
for chari in entryfieldline.strip():
fieldvalue=fieldvalue+chari
if chari ==',':
bibentry[entryfield]=fieldvalue[:-1]
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
enclosenone=False
elif chari =='}':
bibentry[entryfield]=fieldvalue[:-1]
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
enclosenone=False
print('entry:',bibentry)
bibentries.append(bibentry)
bibentry={}
entrystated=False
else:
for chari in entryfieldline.strip():
fieldvalue=fieldvalue+chari
if chari =='{':
counterbracket=counterbracket+1
elif chari =='}':
counterbracket=counterbracket-1
if enclosebracket:
if counterbracket==0:
bibentry[entryfield]=fieldvalue[1:-1]
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
break
elif chari =='"':
counterquotes=counterquotes+1
if not enclosebracket:
if mod(counterquotes,2)==0:
bibentry[entryfield]=fieldvalue[1:-1]
fieldvalue=""
counterbracket=0
counterquotes=0
fieldvalended=True
fieldvalcontinued=False
break
if fieldvalcontinued:
fieldvalended=False
elif line.startswith("@") and "@comment" in line.lower():#@comment的起始
commentsn=commentsn+1
entrynow=line.lstrip('@').split(sep='{', maxsplit=1)
entrytype=entrynow[0]
bibcomment['entrytype']=entrytype.lower()#条目类型小写,方便比较
entrycontents=entrynow[1].strip()[:-1]
bibcomment['entrycontents']=entrycontents
print(bibcomment)
bibcomments.append(bibcomment)
bibcomment={}
elif line.startswith("@") and "@string" in line.lower():#@string的起始
stringsn=stringsn+1
entrynow=line.lstrip('@').split(sep='{', maxsplit=1)
entrytype=entrynow[0]
bibstring['entrytype']=entrytype.lower()#条目类型小写,方便比较
entrycontents=entrynow[1].strip()[:-1]
bibstring['entrycontents']=entrycontents
print(bibstring)
bibstrings.append(bibstring)
bibstring={}
bibentrycounter=len(bibentries)
bibcommentcounter=len(bibcomments)
bibstringcounter=len(bibstrings)
print('entrysn=',entrysn,' commentsn=',commentsn,' stringsn=',stringsn)
print('entryct=',bibentrycounter,' commentct=',bibcommentcounter,' stringct=',bibstringcounter)
if not bibentrycounter==entrysn or not bibcommentcounter==commentsn or not bibstringcounter==stringsn:
try:
raise BibParsingError('bib file parsing went wrong!')
except BibParsingError as e:
raise BibParsingError(e.message)
print('entrytotal=',bibentrycounter)
#输出解析后的bib文件信息
#for bibentryi in bibentries:
#print(bibentryi)
#
#打印bibentries列表中的所有条目
def printbibentries():
for bibentryi in bibentries:
print(bibentryi)
#
#自定义异常类
class BibParsingError(Exception):
def __init__(self,message):
Exception.__init__(self)
self.message=message
#
# 执行数据映射操作
# 还有很多选项没有实现,20190209
# 实现的选项中也需要和未实现的选项进行数据传递
def execsourcemap():
for map in sourcemaps:#every map in maps,每个map逐步开始
for bibentry in bibentries:#每个映射都需要遍历所有条目,对每个条目做map
mapcontinue=1#大于0表示正常,继续当前条目的map
typesrcinfo={}#用于记录typesource相关处理结果
fieldsrcinfo={}#用于记录fieldsource相关处理的结果
constraintinfo={}#用于记录类型等限制信息记录
constraintinfo['pertype']=[]
constraintinfo['pernottype']=[]
#print("map info=",map)
for step in map:#every step in map
if mapcontinue>0:#对前面已经final完成的情况做限制
for keyvals in step:#key-vals in step
for k,v in keyvals.items():#step 是有迹可循的,每个step总是存在一些东西,找到这些做其中的逻辑即可
#print(k,v)
if k=="typesource":#条目类型设置
mapcontinue=maptypesource(keyvals,bibentry,typesrcinfo)#coef is dict
elif k=="fieldsource":#域查找或设置
mapcontinue=mapfieldsource(keyvals,bibentry,fieldsrcinfo,constraintinfo)#
#print("fieldsource step:",fieldsrcinfo)
elif k=="fieldset":#域设置
mapcontinue=mapfieldset(keyvals,bibentry,typesrcinfo,fieldsrcinfo,constraintinfo)#
elif k=="pertype": #类型限制
mapcontinue=mappertype(keyvals,constraintinfo)
elif k=="pernottype":#类型限制
mapcontinue=mappernottype(keyvals,constraintinfo)
elif k=="notfield":#域限制
mapcontinue=mapnotfield(keyvals,bibentry,fieldsrcinfo,constraintinfo)#
else:
pass
#
#域限制,若域确定不存在则终止map
def mapnotfield(keyvals,bibentry,fieldsrcinfo,constraintinfo):
setcontinue=True
print(setcontinue)
if len(constraintinfo['pertype'])>0:
if bibentry['entrytype'] not in constraintinfo['pertype']:#
setcontinue=False
if len(constraintinfo['pernottype'])>0:
if bibentry['entrytype'] in constraintinfo['pernottype']:#
setcontinue=False
print(setcontinue)
print("constraints:notfield")
if setcontinue:
for k,v in keyvals.items():
#print(k,v)
if k=='notfield':
notfield=v.lower()
elif k=='final':
fieldfinal=v
else:
pass
if notfield not in bibentry:#当notfield不存在,则判断为true,若存在final则终止,否则继续
if fieldfinal:
return 0
else:
return 1
else:
return 1
else:
return 0
#
#条目类型限制,将类型限制信息放入字典
def mappertype(keyvals,constraintinfo):
print("constraints:pertype")
for k,v in keyvals.items():
#print(k,v)
if k=='pertype':
pertype=v.lower()#因为大小写区分,所以全部小写方便比较
else:
pass
print('pertype=',pertype)
constraintinfo['pertype'].append(pertype)
return 1
#
#条目类型限制,将类型限制信息放入字典
def mappernottype(keyvals,constraintinfo):
print("constraints:pernottype")
for k,v in keyvals.items():
#print(k,v)
if k=='pernottype':
pernottype=v.lower()#因为大小写区分,所以全部小写方便比较
else:
pass
print('pertype=',pertype)
constraintinfo['pernottype'].append(pernottype)
return 1
#
#条目类型转换
def maptypesource(keyvals,bibentry,typesrcinfo):
retrunval=1
for k,v in keyvals.items():
#print(k,v)
if k=='typesource':
typesource=v.lower()
elif k=='typetarget':
typetarget=v.lower()
elif k=='final':
retrunval=0
else:
pass
typesrcinfo
if typetarget:
if bibentry['entrytype']==typesource:
bibentry['entrytype']=typetarget
typesrcinfo['typesource']=typesource
return 1
else:
return retrunval #存在final时,且不匹配则终止map
else:
if bibentry['entrytype']==typesource:
typesrcinfo['typesource']=typesource
else:
typesrcinfo['typesource']=None
return retrunval
#
#
# 域信息设置
def mapfieldset(keyvals,bibentry,typesrcinfo,fieldsrcinfo,constraintinfo):
overwrite=False
append=False
setcontinue=True
print(setcontinue)
if len(constraintinfo['pertype'])>0:
if bibentry['entrytype'] not in constraintinfo['pertype']:#
setcontinue=False
if len(constraintinfo['pernottype'])>0:
if bibentry['entrytype'] in constraintinfo['pernottype']:#
setcontinue=False
print(setcontinue)
if setcontinue:
for k,v in keyvals.items():
#print(k,v)
if k=='fieldset':
fieldset=v.lower()
elif k=='fieldvalue':
fieldvalue=v
elif k=='origfieldval':
fieldvalue=fieldsrcinfo[fieldsrcinfo['fieldsource']]
print(fieldvalue)
elif k=='origentrytype':
fieldvalue=typesrcinfo['typesource']
print(fieldvalue)
elif k=='origfield':
fieldvalue=fieldsrcinfo['fieldsource']
print(fieldvalue)
elif k=='null':
fieldvalue=None
print(fieldvalue)
elif k=='append':
append=v
elif k=='overwrite':
overwrite=v
else:
pass
print("fieldset=",fieldset)
if overwrite:
if append:
oldvalue=bibentry[fieldset]
newvalue=oldvalue+","+fieldvalue
bibentry[fieldset]=newvalue
else:
bibentry[fieldset]=fieldvalue
else:
if fieldset in bibentry:
pass
else:
bibentry[fieldset]=fieldvalue
return 1
else:
return 1
#
#域名转换或判断域是否存在或match
def mapfieldsource(keyvals,bibentry,fieldsrcinfo,constraintinfo):
mapfieldtype=0 #域名map的类型设置
fieldmatch=''#匹配模式默认为空
fieldmatchi=''#匹配模式默认为空
setcontinue=True
print(setcontinue)
if len(constraintinfo['pertype'])>0:
if bibentry['entrytype'] not in constraintinfo['pertype']:#
setcontinue=False
if len(constraintinfo['pernottype'])>0:
if bibentry['entrytype'] in constraintinfo['pernottype']:#
setcontinue=False
print(setcontinue)
overwrite=False
for k,v in keyvals.items():
#print(k,v)
if k=='fieldsource':
fieldsource=v.lower()
elif k=='fieldtarget':
fieldtarget=v.lower()
mapfieldtype=1 #域map类型1,直接做域名转换
elif k=='replace':
fieldreplace=v
mapfieldtype=2 #域map类型2,直接做域信息转换
elif k=='final':
fieldfinal=v #final要么选项不给出,要么选项给出为true
if fieldfinal:#当为true时则做终止判断
mapfieldtype=3 #域map类型3,做final判断可以终止map
elif k=='overwrite':#
overwrite=v
elif k=='match':#不区分大小写的match
fieldmatch=v
elif k=='matchi':#区分大小写的match
fieldmatchi=v
elif k=='notmatch':#不区分大小写的notmatch,下面这两个选项的逻辑没有实现
fieldnotmatch=v
elif k=='notmatchi':#区分大小写的notmatch
fieldnotmatch=v
else:
pass
if setcontinue:
#返回字典的第一项是fieldsource信息
fieldsrcinfo['fieldsource']=fieldsource
if mapfieldtype==0:#第0中情况即,不做信息转换,也不终止map,仅返回一些信息
if fieldsource in bibentry:
if fieldmatch:
m = re.match(fieldmatch, bibentry[fieldsource])
if m:
fieldsrcinfo[fieldsource]=[bibentry[fieldsource],fieldmatch]
else:
fieldsrcinfo[fieldsource]=[None] #正则不匹配,则返回为None
elif fieldmatchi:
m = re.match(fieldmatchi, bibentry[fieldsource])
if m:
fieldsrcinfo[fieldsource]=[bibentry[fieldsource],fieldmatchi]
else:
fieldsrcinfo[fieldsource]=[None] #正则不匹配,则返回为None
else:
fieldsrcinfo[fieldsource]=bibentry[fieldsource]#将域的值记录下来,用于下一step
else:
fieldsrcinfo[fieldsource]=[None] #域不存在则返回为None
return 1
elif mapfieldtype==1:#第1中情况即,做域名转换
print('fieldsource=',fieldsource,'fieldtarget=',fieldtarget)
if fieldsource in bibentry:
bibentry[fieldtarget]=bibentry[fieldsource]
del bibentry[fieldsource]
fieldsrcinfo[fieldsource]=bibentry[fieldtarget]
else:
fieldsrcinfo[fieldsource]=[None]
return 1
elif mapfieldtype==2:#域map类型2,直接做域信息转换,真正操作需要overwrite选项支持
print('fieldsource=',fieldsource)
print('fieldmatch=',fieldmatch)
print('fieldreplace=',fieldreplace)
if overwrite:
if fieldsource in bibentry:
if fieldmatch:
#strafrpl=re.sub(r'(\d\d\d\d)\-(\d)\-(\d)',r'\1-0\2-0\3','2015-1-3')
#strafrpl=re.sub(r'(\d\d\d\d)\-(\d)\-(\d)',r'\1-0\2-0\3',bibentry[fieldsource])
strafrpl=re.sub(fieldmatch,fieldreplace,bibentry[fieldsource]) #执行替换
print('strafterreplace=',strafrpl)
bibentry[fieldsource]=strafrpl
elif fieldmatchi:
strafrpl=re.sub(fieldmatchi, fieldreplace,bibentry[fieldsource])
bibentry[fieldsource]=strafrpl
else:
fieldsrcinfo[fieldsource]=[None]
else:
fieldsrcinfo[fieldsource]=[None]
else:
fieldsrcinfo[fieldsource]=[None]
return 1
elif mapfieldtype==3:#域map类型3,当没有匹配则终止map
if fieldsource in bibentry:
if fieldmatch:
m = re.match(fieldmatch, bibentry[fieldsource])
if m:
fieldsrcinfo[fieldsource]=[bibentry[fieldsource],fieldmatch]
return 1
else:
return 0
elif fieldmatchi:
if m:
fieldsrcinfo[fieldsource]=[bibentry[fieldsource],fieldmatchi]
return 1
else:
return 0
else:
fieldsrcinfo[fieldsource]=bibentry[fieldsource]#将域的值记录下来,用于下一step
return 1
else:
return 0
else:
fieldsrcinfo[fieldsource]=[None]
return 1
#运行脚本测试
if __name__=="__main__":
#设置需要修改的bib文件
#inputbibfile='example.bib'
#inputbibfile='biblatex-map-test.bib'
inputbibfile='eg-thesis.bib'
#auxfile="opt-gbpub-true.aux"
#set the aux file
#this is not necessary
auxfile=''
#auxfile="opt-gbpub-true.aux"
readfilecontents(inputbibfile)
#printfilecontents()
bibentryparsing()
#printbibentries()
execsourcemap()
#printbibentries()
writefilenewbib(inputbibfile)
#print(bibentries[0])
#print(formatbibentry(bibentries[0]))
formatallbibliography()
printbibliography()