关于python:懒惰的美德我用-python-写了个自动生成给文档生成索引的脚本

2次阅读

共计 5799 个字符,预计需要花费 15 分钟才能阅读完成。

我用 python 写了一个主动生成索引的脚本

简介:为了刷算法题,建了一个 GitHub 仓库:PiperLiu / ACMOI_Journey,记录本人的刷题轨迹,并总结一下办法、心得。想到一个需要:能不能在我每新增一条题目的笔记后,利用程序主动地将其归类、创立索引?用 Python 实现一个入门级的小脚本,波及到 文件读写、命令行参数、数组操作利用等知识点,在此分享给敌人们。

需要实现

我有一个 Markdown 文档,长成上面这个样子:

# ACM/OI Journey
在此留下刷题痕迹与刷题心得。不定期的方法论总结在这里[./notes/README.md](./notes/README.md)。学习材料:- OI Wiki: https://oi-wiki.org/
- 力扣中国: https://leetcode-cn.com/

## 归档
## 日期归档

留神到,两个二级题目 ## 归档## 日期归档 下空洞无物。

我的需要是,我刷完一道题,就将其记录在 ## 日期归档 下,格局为:– uu 日期 题目名称与概括 类别 A 类别 B 类别 C … [程序文件 1] [程序文件 2] [程序文件 3]…

假如我明天刷了 2 道题,那么我就将其记录在我的 ## 日期归档 上面,如下所示。

## 日期归档
- uu 2020.11.26 盛最多水的容器『因为两个边独特决定了下限,因而将较短边向内挪动,摈弃搜寻次优解』双指针法 搜寻 (./vsc_leetcode/11. 盛最多水的容器.py) (./vsc_leetcode/11. 盛最多水的容器.cpp)
- uu 2020.11.27 整数转罗马数字『生存中从大的位数开始形容数字,因而从大的数与字符开始匹配』匹配 字符串 (./vsc_leetcode/12. 整数转罗马数字.cpp)

而我的 ## 归档 上面还什么都没有,我心愿我的脚本能够主动帮我在 ## 归档 下创立三级目录:双指针法 搜寻 匹配 字符串,并且将对应的题目放到上面去。

最终的成果是:

## 归档
- [匹配](# 匹配)
- [字符串](# 字符串)
- [双指针法](# 双指针法)
- [搜寻](# 搜寻)
### 匹配
- 整数转罗马数字『生存中从大的位数开始形容数字,因而从大的数与字符开始匹配』(./vsc_leetcode/12. 整数转罗马数字.cpp) 2020.11.27

### 字符串
- 整数转罗马数字『生存中从大的位数开始形容数字,因而从大的数与字符开始匹配』(./vsc_leetcode/12. 整数转罗马数字.cpp) 2020.11.27

### 双指针法
- 盛最多水的容器『因为两个边独特决定了下限,因而将较短边向内挪动,摈弃搜寻次优解』(./vsc_leetcode/11. 盛最多水的容器.py) (./vsc_leetcode/11. 盛最多水的容器.cpp) 2020.11.26

### 搜寻
- 盛最多水的容器『因为两个边独特决定了下限,因而将较短边向内挪动,摈弃搜寻次优解』(./vsc_leetcode/11. 盛最多水的容器.py) (./vsc_leetcode/11. 盛最多水的容器.cpp) 2020.11.26

## 日期归档
- 2020.11.26 盛最多水的容器『因为两个边独特决定了下限,因而将较短边向内挪动,摈弃搜寻次优解』双指针法 搜寻 (./vsc_leetcode/11. 盛最多水的容器.py) (./vsc_leetcode/11. 盛最多水的容器.cpp)
- 2020.11.27 整数转罗马数字『生存中从大的位数开始形容数字,因而从大的数与字符开始匹配』匹配 字符串 (./vsc_leetcode/12. 整数转罗马数字.cpp)

通过 Markdown 引擎渲染后的成果如下图。

如上,我岂但新增了三级题目 ### 匹配### 字符串 等,还为三级题目创立了目录索引链接。

最终程序实现如下图。

Python 与脚本文件

这样就要派上咱们的 Python 出场了。我感觉这才是 Python 的老本行:脚本文件。记得 Python 猫已经有篇文章,讲过为什么 Python 中的正文符号是 # 而不是 //

起因很可能是:Python 的老本行,就是写这一个个易用的脚本文件的,与 shell 相似。

想想 Python 的特点:解释型语言、动静型语言、在命令行里能够一条一条地输出、os.system()能够间接调用命令 … 所以,拿 Python 来执行一个个小工作(脚本文件)再适合不过了。

整体逻辑

逻辑是:

  • 先把文件读到内存中,以列表 list 的模式保留
  • 列表 list 内,每一元素对应一句话
  • 遍历列表,遇到元素 ## 归档 则其之后的元素依照不同条件取出、剖析
  • 直到遇到元素## 日期归档,则把其之后的元素按条件取出、剖析

细节在代码里(代码文件refresh.py),我应用汉语表明了。

""""""
import os.path as osp
import re
def refreah():
    """
    我要解决的文件是 README.md
    那么我获取其绝对路径
    留神这里解决的文件和代码文件处于同一目录下
    """
    dirname = osp.dirname(__file__)
    filepath = osp.join(dirname, "README.md")

    """关上这个文件,其变量名是 f"""
    with open(filepath, 'r+', encoding='utf-8') as f:
        """将文件的内容读到内存 f.read()"""
        content = f.read()
        """
        以“换行符”/“回车”进行字符串宰割
        这样,row_list 每个元素就是一行文字了
        """row_list = content.split('\n')"""
        上面开始把不同的目录对应的条目取出
        """
        # found the un-packed row
        un_packed_rows = []
        dict_cata = {}
        dict_row_flag = False
        date_row_flag = False
        dict_row_num  = 0
        date_row_num  = 0
        cur_cata = None
        for idx, row in enumerate(row_list):
            """如果到了 ## 归档 上面"""
            if dict_row_flag:
                if "###" in row[:4]:
                    cur_cata = row[4:]
                    """
                    data_cata 是咱们的类别字典,最终成果为
                    data_cata = {"匹配": [匹配的第 1 题, 匹配的第 2 题, ...],
                        "字符串": [字符串的第 1 题, 字符串的第 2 题, ...],
                        ...
                    }
                    """
                    dict_cata.setdefault(cur_cata, [])
                elif "-" in row[:2] and not re.match('\[.*\]\(.*\)', row[2:]):
                    """
                    这里用了一个正则
                    因为索引格局为
                        - [索引名称](# 索引名称)
                    而题目格局为
                        - 题目 程序 日期
                    因而如果仅凭是否以「-」结尾,则难以辨别二者
                    因而加了一个是否正则匹配 [*](*) 的判断
                    """
                    dict_cata[cur_cata] = [row] + dict_cata[cur_cata]
            else:
                """判断是否到了 ## 归档 上面"""
                if row == "## 归档":
                    dict_row_flag = True
                    dict_row_num  = idx + 1
            """如果到了 ## 日期归档 上面"""
            if date_row_flag:
                """
                - uu 是我本人设的格局
                如果题目有 uu,那么这条就是我要用脚本加到归档里的题目
                """if'- uu ' in row[:5]:
                    un_packed_rows = [row] + un_packed_rows
                    row_list[idx] = "-" + row[5:]
            else:
                """判断是否到了 ## 日期归档 上面"""
                if row == "## 日期归档":
                    date_row_flag = True
                    dict_row_flag = False
                    date_row_num  = idx + 1
        # pack those rows to "## 日期归档"
        """上面是把新题目(uu)加到 data_cata 字典中"""
        for row in un_packed_rows:
            row = row.split(' ')
            file_num = 0
            file_name = ""
            for ele in row:
                if re.match('\[.*\]\(.*\)', ele):
                    file_num += 1
                    file_name += (ele + ' ')
            catas = row[4:-file_num]
            for c in catas:
                dict_cata.setdefault(c, [])
                row_ = '-' + row[3] + ' ' + file_name + row[2]
                dict_cata.append(row_)
        # del file "## 归档"
        """
        上面是清空 ## 归档 的内容
        依据 dict_cata 书写新的全部内容
        """
        row_list_a = row_list[:dict_row_num]
        row_list_c = row_list[date_row_num-2:]
        ## row_list_b
        row_list_b = []
        for key in dict_cata:
            row_list_b.append("\n###" + key)
            for row in dict_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
    
    """
    把新解决好的文本,逐行写到文件中(文件先清空,原文本被笼罩)"""with open(filepath,'w', encoding='utf-8') as f:
        for row in row_list:
            f.write(row + '\n')
    
    """提醒用户,解决好了"""
    print("\033[1;34mREADME.md refresh done\033[0m")
    print("\033[1;36mhttps://github.com/PiperLiu/ACMOI_Journey\033[0m")
    print("star"
        + "\033[1;36m the above repo \033[0m"
        + "and practise together!")

def cata_index():
    """
    这是我用于生成索引的函数
    索引就是:## 归档
    - [匹配](# 匹配)
    - [字符串](# 字符串)
    - [双指针法](# 双指针法)
    - [搜寻](# 搜寻)

    思路很简略,还是取各个三级题目
    而后规整到 ## 归档 上面
    """
    dirname = osp.dirname(__file__)
    filepath = osp.join(dirname, "README.md")

    with open(filepath, 'r+', encoding='utf-8') as f:
        content = f.read()
        row_list = content.split('\n')
        cata_list = []
        dict_row_flag = False
        dict_row_num  = 0
        cata_row_num  = 0
        for idx, row in enumerate(row_list):
            if dict_row_flag:
                if cata_row_num == 0:
                    cata_row_num = idx
                if "###" in row[:4]:
                    cata = row[4:]
                    cata = "- [" + cata + "]" + "(#" + cata + ")"
                    cata_list.append(cata)
            elif row == "## 归档":
                dict_row_flag = True
                dict_row_num  = idx + 1
            elif row == "## 日期归档":
                cata_list.append("\n")
                break
        # add idx
        row_list_a = row_list[:dict_row_num]
        row_list_c = row_list[cata_row_num:]
        row_list = row_list_a + cata_list + row_list_c
        with open(filepath, 'w', encoding='utf-8') as f:
            for row in row_list:
                f.write(row + '\n')

refresh()
cata_index()

最终的运行成果是,我在命令行执行该脚本,则文档主动规整。

argparse 利用

留神到下面我输出了一个参数 -r,这个是为了让 refresh.py 这个文件有更多功能,并且在不同参数时做不同的事。参数好像不同的「按钮」。

我将各个性能封装在不同函数中,将利用解耦,即不同性能间不相互依赖,防止出现逻辑谬误。

此外,我新建了一个函数,用于获取参数。

def get_args():
    parser = argparse.ArgumentParser()

    parser.add_argument(
        '--refresh', '-r',
        action='store_true',
        help='refreah README.md'
    )

    args = parser.parse_known_args()[0]
    return args

这样,咱们就能够获取到 -r 这个参数,在主过程里,咱们判断用户是否应用 r 这个性能,应用的话,则调用相应函数。

def main(args=get_args()):
    if args.refresh:
        refreah()
        cata_index()

if __name__ == "__main__":
    main()

注意事项:encoding

此外,因为是中文,因而编码规定值得注意。

比方,在文件结尾退出 #-*- coding:UTF-8 -*-;在 open 文件时,退出 encoding='uft-8' 参数。

值得改良的点:更好的正则

如果你读我的代码,你会发现读取、判断行的逻辑上有些“粗犷”。

仅仅通过判断 - [] 等是否是行的前四个字符是不妥的,并且我在判断 - uu 日期 题目名称与概括 类别 A 类别 B 类别 C... [程序文件 1] [程序文件 2] [程序文件 3]... 时,也仅仅是通过 if else 判断是否有方括号、括号来辨别 类别字段 程序文件 字段。

这是不妥的,这样,我就难以在题目里自在书写。一个可行的改良,是应用弱小的正则表达式进阶属性。

尚无精力探讨,将来可能会进一步批改探讨,欢送继续关注我。

我的项目地址:https://github.com/PiperLiu/A…

欢送 star watch fork pr issue 五连。

正文完
 0