我用python写了一个自动生成索引的脚本记录下自己的刷题轨迹,总结一下自己的方法和经验。我想到了一个需求:是否可以在添加一个主题的注释后使用程序自动分类创建索引?使用Python实现一个入门级小脚本,涉及文件读写、命令行参数、数组操作应用等知识点。在这里与您的朋友分享。RequirementRealization我有一个Markdown文档,大概是这样的:#ACM/OIJourney就在这里留下刷题的痕迹和经验。偶尔的方法总结在这里[./notes/README.md](./notes/README.md)。学习资料:-OIWiki:https://oi-wiki.org/-力扣中国:https://leetcode-cn.com/##Archive##DateArchive注意这里有两个副标题##Archive文件##日期为空。我的要求是做完一道题后,记录在##日期存档下,格式为:-uu日期标题名称和摘要分类A类B类C...【程序文件1】【程序文件2】【程序文件3】...假设我今天刷了2道题,然后记录在我的##日期文件下,如下图。##日期存档-uu2020.11.26盛水最多的容器《因为两边共同确定上限,所以短边向内移动,放弃对次优解的搜索》双指针法搜索[py](./vsc_leetcode/11.盛水最多的容器.py)[cpp](./vsc_leetcode/11.盛水最多的容器.cpp)-uu2020.11.27整数转罗马数字》描述数字开始从生命中最大的数字开始,所以从大数和字符开始匹配”匹配字符串[cpp](./vsc_leetcode/12.IntegertoRomannumerals.cpp)而我的##文件下没有任何东西,希望我的脚本可以自动帮我在##存档下创建一个三级目录:双指针法、搜索、匹配、字符串,并把相应的主题放在下面。最终结果是:##archive-[match](#match)-[string](#string)-[双指针法](#doublepointermethod)-[search](#search)###match-IntegertoRomanNumerals"在生活中,数字都是从大数字开始描述的,所以大数字和字符开始匹配"[cpp](./vsc_leetcode/12.IntegertoRomannumerals.cpp)2020.11.27###String-整数转罗马数字《生活中数字的描述都是从大量的数字开始的,所以匹配都是从大量的数字和字符开始的》[cpp](./vsc_leetcode/12.整数转罗马数字.cpp)2020.11.27###双指针法——盛水最多的容器《因为两侧共同决定上限,短边向内移动,放弃寻找次优解》[py](./vsc_leetcode/11.装水最多的容器Container.py)[cpp](./vsc_leetcode/11.装水最多的容器.cpp)2020.11.26###搜索-装水最多的容器『Beca用两条边共同确定上限,短边向内移动,放弃对次优解的搜索』[py](./vsc_leetcode/11.盛水最多的容器.py)[cpp](./vsc_leetcode/11.盛水最多的容器.cpp)2020.11.26##日期存档-2020.11.26盛水最多的容器"因为双方共同确定上限,所以将短边向内移动,并且放弃对次优解的搜索”双指针法搜索[py](./vsc_leetcode/11.装水最多的容器.py)[cpp](./vsc_leetcode/11.装水最多的容器.cpp)-2020.11.27整数转罗马数字"生活中,数字都是从最大的数字开始描述的,所以从最大的开始匹配数字和字符"匹配字符串[cpp](./vsc_leetcode/12.Integer到RomanNumeral.cpp)由Markdown引擎渲染,如下所示。如上,我不仅添加了三级标题###匹配、###字符串等,还为三级标题创建了一个目录索引链接。最终程序实现如下图所示。Python和脚本文件这就是我们的Python的用武之地。我认为这就是Python的全部意义所在:脚本文件。记得Python猫曾经有一篇文章讲为什么Python中的注释符号是#而不是//。原因很可能是:Python的老本行就是写这些好用的脚本文件,类似于shell。想想Python的特点:解释型语言,动态语言,命令行输入一个一个,os.system()可以直接调用命令...所以,用Python来执行小任务(脚本文件)是不行的更合适。整体的逻辑逻辑是:先将文件读入内存,将list以list列表的形式保存,每个元素对应一个语句遍历list,遇到元素##则取其后的元素根据不同条件取出并分析直到遇到元素##日期存档时,根据条件取出其后的元素,具体分析在代码中(代码文件refresh.py),我用中文标注.""""""importos.pathasospimportredefrerefreah():"""我要处理的文件是README.md,所以得到它的绝对路径,注意这里处理的文件和代码文件。"""dirname=osp.dirname(__file__)filepath=osp.join(dirname,"README.md")"""打开这个文件,变量名为f"""withopen(filepath,'r+',encoding='utf-8')asf:"""将文件内容读入内存f.read()"""content=f.read()"""用"换行符分割字符串"/"回车"这样,row_list的每个元素都是一行文本"""row_list=content.split('\n')"""接下来开始提取不同目录对应的条目"""#找到未打包的行un_packed_rows=[]dict_cata={}dict_row_flag=Falsedate_row_flag=Falsedict_row_num=0date_row_num=0cur_cata=Noneforidx,rowinenumerate(row_list):"""ifitisbelow##archive"""ifdict_row_flag:if"###"inrow[:4]:cur_cata=row[4:]"""data_cata就是我们的类别字典,最后的效果就是data_cata={"match":[匹配第一题,匹配第二题,...],"string":[字符串第一题,字符串的第二题,...],...}"""dict_cata.setdefault(cur_cata,[])elif"-"inrow[:2]而不是re.match('\[.*\]\(.*\)',row[2:]):"""这里用了正则表达式,因为索引格式是-[索引名称](#索引名称),标题格式是-标题程序日期,所以如果只看是否以开头"-",两者很难区分,所以加了判断是否是正则匹配[*](*)"""dict_cata[cur_cata]=[row]+dict_cata[cur_cata]else:"""判断是否在##file下面"""ifrow=="##file":dict_row_flag=Truedict_row_num=idx+1"""如果在##datearchive下面"""ifdate_row_flag:"""-uu是我自己设置的格式如果标题有uu,那么这就是我想用脚本添加到存档中的标题"""if'-uu'inrow[:5]:un_packed_rows=[row]+un_packed_rowsrow_list[idx]="-"+row[5:]else:"""判断是否为##日期存档below"""ifrow=="##DateArchive":date_row_flag=Truedict_row_flag=Falsedate_row_num=idx+1#packthoserowsto"##DateArchive""""下面是添加新标题(uu)到data_cata字典"""forrowinun_packed_rows:row=row.split('')file_num=0file_name=""foreleinrow:ifre.match('\[.*\]\(.*\)',ele):file_num+=1file_name+=(ele+'')catas=row[4:-file_num]forcincatas:dict_cata.setdefault(c,[])row_='-'+row[3]+''+file_name+row[2]dict_cata[c].append(row_)#delfile"##archive""""下面是清空##archive内容根据dict_cata写入新的所有内容"""row_list_a=row_list[:dict_row_num]row_list_c=row_list[date_row_num-2:]##row_list_brow_list_b=[]forkeyindict_cata:row_list_b.append("\n###"+key)forrowindict_cata[key]:row_list_b.append(row)row_list_b[0]=row_list_b[0][1:]row_list=row_list_a+row_list_b+row_list_c"""将新处理的文本逐行写入文件(先清空文件,覆盖原文本)"""withopen(filepath,'w',encoding='utf-8')asf:forrowinrow_list:f.write(row+'\n')"""提示用户,已处理"""print("\033[1;34mREADME.md刷新完成\033[0m")print("\033[1;36mhttps://github.com/PiperLiu/ACMOI_Journey\033[0m")print("star"+"\033[1;36m以上repo\033[0m"+"一起练习!")defcata_index():"""这是我用来生成索引的函数索引:##archive-[match](#match)-[string](#string)-[doublepointermethod](#doublepointermethod)-[search](#搜索)思路很简单,还是把三级标题各取一个,组织到##存档中"""dirname=osp.dirname(__file__)filepath=osp.join(dirname,"README.md")withopen(filepath,'r+',encoding='utf-8')asf:content=f.read()row_list=content.split('\n')cata_list=[]dict_row_flag=Falsedict_row_num=0cata_row_num=0foridx,rowinenumerate(row_list):ifdict_row_flag:ifcata_row_num==0:cata_row_num=idxif"###"inrow[:4]:cata=row[4:]cata="-["+cata+"]"+"(#"+cata+")"cata_list.append(cata)elifrow=="##返回file":dict_row_flag=Truedict_row_num=idx+1elifrow=="##datefile":cata_list.append("\n")break#addidxrow_list_a=row_list[:dict_row_num]row_list_c=row_list[cata_row_num:]row_list=row_list_a+cata_list+row_list_cwithopen(filepath,'w',encoding='utf-8')asf:forrowinrow_list:f.write(row+'\n')refresh()cata_index()final的运行效果是我在命令行执行脚本,argparse应用程序注意到我在上面输入了一个参数-r,这是为了让refresh.py文件有更多的功能,不同的参数做不同的事情used.参数就??像是“按钮”,有不同的参数,我把每个函数封装在不同的函数中,实现应用程序的解耦,即不同的函数之间不依赖,防止逻辑错误。另外,我新建了一个函数来获取parameters.defget_args():parser=argparse.ArgumentParser()parser.add_argument('--refresh','-r',action='store_true',help='refreahREADME.md')args=parser.parse_known_args()[0]returnargs这样我们就可以得到-r参数了。在主流程中,我们判断用户是否使用了r函数,如果是,则调用相应的函数。defmain(args=get_args()):ifargs.refresh:refresh()cata_index()if__name__=="__main__":main()注意:编码另外因为是中文,编码规则值得注意。例如在文件开头添加#-*-coding:UTF-8-*-;打开文件时添加encoding='uft-8'参数。值得改进的点:更好的正则化如果你读过我的代码,你会发现读取和判断行的逻辑有点“粗糙”。只是判断-[]等是否是该行的前四个字符是错误的,我在判断-uu日期标题名称与一般类别A类别B类别C...[程序文件1][程序文件2][Programfile3]...,只有通过ifelse判断是否有方括号或括号来区分类别字段和programfile字段。这是不合适的,所以我很难在标题中随意写。一个可能的改进是使用强大的正则表达式高级属性。目前还没有精力讨论,以后可能会进一步修改讨论。欢迎继续关注我。项目地址:https://github.com/PiperLiu/A...欢迎关注starwatchforkprissuewulian。
