Markdown标题自动添加编号

markdown,标题,自动,添加,编号 · 浏览次数 : 438

小编点评

```go package main import ( "os" "os/ioutil" "fmt" "strconv" "strings" ) // ChangedLine struct is used to store information about changed line. type ChangedLine struct { LineNo int Before string After string } // getAllLines function reads all lines from a file and returns them as a string slice. func getAllLines(fp string) ([]string, error) { var lines []string file, err := os.Open(fp, os.RDONLY) if err != nil { return nil, err } defer file.Close() reader := bufio.NewReader(file) for { line, err := reader.ReadString('\n') if err == io.EOF { break } lines = append(lines, line) } return lines, nil } // addTitle function adds a title with specified level to the lines slice. func addTitle(lines []string) ([]string, []ChangedLine) { // Support for up to 5 levels of titles maxLevels := 5 titles := [5]int{0, 0, 0, 0, 0} newLines := []string{} changedLines := []ChangedLine{} // Iterate through the lines and add title based on level for i, line := range lines { for j := 0; j < maxLevels; j++ { if strings.HasPrefix(line, fmt.Sprintf("%%d", j+1)) { newLines = append(newLines, line) changedLines = append(changedLines, ChangedLine{ LineNo: i + 1, Before: line, After: line, }) break } } } return newLines, changedLines } // writeLines function writes multiple lines of data to a file. func writeLines(fp string, lines []string) error { content := strings.Join(lines, "\n") return ioutil.WriteFile(fp, []byte(content), 0644) } // main function handles the command line arguments and runs the script. func main() { rootCmd := &cobra.Command{ Use: "mt", Short: "给mkdown标题添加编号", RunE: func(cmd *cobra.Command, args []string) error { // Get file name from command line fp := args[0] // Get lines from the file lines, err := getAllLines(fp) if err != nil { return err } // Add title to the lines newLines, changedLines := addTitle(lines) // Print changes to console fmt.Println("修改的内容:>>>"); for _, cl := range changedLines { fmt.Println("===================================\n") fmt.Printf("line【%d】:\修改前:%s\修改后:%s\\\n", cl.LineNo, cl.Before, cl.After) } // Print success message return nil }, } rootCmd.Execute() } ```

正文

markdown写文档很方便,但是有个困扰的地方,就是标题的编号问题。
写文档的时候,经常会在中间插入新的标题和内容,所以手动管理编号的话,如果新的标题插在前面,则要调整后面所有的编号。

如果在文档完成后再手动加上编号的话,不仅容易忘记,
而且有时候我们是在其他编辑器里编辑文档再导出markdown的,比如用语雀编写文档再导出markdown,这时每次修改文档再导出就要重新给导出的文档添加编号。
我用语雀比较多,常常因此而困扰。

所以,用golang简单写了个命令行工具,用来给markdown文档的标题添加编号。

1. 处理流程

处理过程很简单:

  1. 首先读取markdown文件的所有行
  2. 然后依次解析每一行
    1. 如果是标题行,添加编号
    2. 非标题行,略过
  3. 将添加了编号的内容重新写入markdown文件

2. 主要步骤

主要的步骤有三个:

2.1. 读取markdown文件

// 获取文件所有的行
func getAllLines(fp string) ([]string, error) {
    fi, err := os.Open(fp)
    if err != nil {
        return nil, err
    }
    defer fi.Close()

    br := bufio.NewReader(fi)
    lines := make([]string, 0)
    for {
        a, _, c := br.ReadLine()
        if c == io.EOF {
            break
        }
        lines = append(lines, string(a))
    }

    return lines, nil
}

返回的是文件所有行的数组。

2.2. 添加标题编号

// 添加标题编号,最多支持五级标题
func addTitle(lines []string) ([]string, []ChangedLine) {
	cLines := make([]ChangedLine, 0)
	titles := [5]int{0, 0, 0, 0, 0}

	for index, line := range lines {
		titleLevel := getTitleLevel(line)
		switch titleLevel {
		case 1:
			titles[0]++
			lines[index] = strings.Replace(line, "# ", fmt.Sprintf("# %d. ", titles[0]), 1)
			titles = [5]int{titles[0], 0, 0, 0, 0}
		case 2:
			titles[1]++
			lines[index] = strings.Replace(line, "## ", fmt.Sprintf("## %d.%d. ", titles[0], titles[1]), 1)
			titles = [5]int{titles[0], titles[1], 0, 0, 0}
		case 3:
			titles[2]++
			lines[index] = strings.Replace(line, "### ", fmt.Sprintf("### %d.%d.%d. ", titles[0], titles[1], titles[2]), 1)
			titles = [5]int{titles[0], titles[1], titles[2], 0, 0}
		case 4:
			titles[3]++
			lines[index] = strings.Replace(line, "#### ", fmt.Sprintf("#### %d.%d.%d.%d. ", titles[0], titles[1], titles[2], titles[3]), 1)
			titles = [5]int{titles[0], titles[1], titles[2], titles[3], 0}
		case 5:
			titles[4]++
			lines[index] = strings.Replace(line, "##### ", fmt.Sprintf("##### %d.%d.%d.%d.%d. ", titles[0], titles[1], titles[2], titles[3], titles[4]), 1)
			titles = [5]int{titles[0], titles[1], titles[2], titles[3], titles[4]}
		}

		if titleLevel != -1 {
			cLines = append(cLines, ChangedLine{LineNo: index + 1, Before: line, After: lines[index]})
		}
	}

	return lines, cLines
}

这里支持最多5级标题的编号,写的略显繁琐,本身逻辑比较简单,暂时没有去优化。

获取标题的等级写了简单的小函数:
(根据markdown的语法,根据每行开头 # 的个数来判断是几级的标题)

// 获取标题的等级
func getTitleLevel(s string) int {
	if strings.HasPrefix(s, "# ") {
		return 1
	}
	if strings.HasPrefix(s, "## ") {
		return 2
	}
	if strings.HasPrefix(s, "### ") {
		return 3
	}
	if strings.HasPrefix(s, "#### ") {
		return 4
	}
	if strings.HasPrefix(s, "##### ") {
		return 5
	}

	return -1
}

2.3. 新内容写入markdown文件

// 写入多行数据
func writeLines(fp string, lines []string) error {
	content := strings.Join(lines, "\n")
	return ioutil.WriteFile(fp, []byte(content), 0644)
}

2.4. 步骤合并起来

此命令行工具使用了 cobra 框架,最后把修改的部分也打印出来了。

type ChangedLine struct {
	LineNo int
	Before string
	After  string
}

var rootCmd = &cobra.Command{
	Use:   "mt",
	Short: "给mkdown标题添加编号",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) < 1 {
			return fmt.Errorf("NO file input!")
		}
		fp := args[0]
		lines, err := getAllLines(fp)
		if err != nil {
			return err
		}

		newLines, changedLines := addTitle(lines)
		err = writeLines(fp, newLines)
		if err != nil {
			return err
		}

		fmt.Println("修改的内容:>>>")
		for _, cl := range changedLines {
			fmt.Println("===================================")
			fmt.Printf("line【%d】:\n修改前:%s\n修改后:%s\n", cl.LineNo, cl.Before, cl.After)
			fmt.Println("===================================")
		}

		return nil
	},
}

3. 使用方法

go build  # 编译后生成 mdtitle 二进制文件
./mdtitle xxxx.md   # 运行

本文最后附加的下载地址中有完整源码。
其中也包含编译好的二进制(linux和windows版本2个都有)。

4. 补充说明

今天一时起意写的小工具,只是为了方便给自己从语雀导出的markdown加标题编号。
一定还有很多不足之处有待完善,写完之后我自己感觉至少还需要完善:

  1. 没有判断文档是否已经有标题编号,已经有标题编号的情况,应当忽略继续添加标题编号
  2. 标题的层级是硬编码的,目前只支持5级标题(不过5级应该满足大部分情况了)

代码下载地址(其中包含编译好的linux和windows下的二进制):
md-title.zip: https://url11.ctfile.com/f/45455611-859852761-9f5f8e?p=6872
(访问密码: 6872)

与Markdown标题自动添加编号相似的内容:

Markdown标题自动添加编号

用`markdown`写文档很方便,但是有个困扰的地方,就是标题的编号问题。 写文档的时候,经常会在中间插入新的标题和内容,所以手动管理编号的话,如果新的标题插在前面,则要调整后面所有的编号。 如果在文档完成后再手动加上编号的话,不仅容易忘记, 而且有时候我们是在其他编辑器里编辑文档再导出`mark

给picgo上传的图片加个水印

之前给大家介绍了picgo和免费的图床神器。我们本可以开开心心的进行markdown写作了。 但是总是会有那么一些爬虫网站过来爬你的文章,还把你的文章标明是他们的原著。咋办呢?这里有一个好的办法就是把markdown中上传的图片加上自己的水印不就行了。 说干就干。接下来我会介绍如何在picgo中进行

Typora 最新中文版安装破解V1.4.8

Typora中文破解版是一款好用极简免费的跨平台Markdown编辑器,软件使用这款软件能够帮助用户轻松将文本转换到HTML,软件从底层向上设计,软件支持markdown的标准语法,同时这款软件还支持动态预览功能,一键预览,让一切都变得如此干净、纯粹,是一款不可多得的优质markdown编辑器。 T

Spring源码之XML文件中Bean标签的解析2

### 读取XML文件,创建默认bean标签对象的核心代码 ```markdown 在DefaultBeanDefinitionDocumentReader类中的如下方法中: protected void processBeanDefinition(Element ele, BeanDefiniti

C#实现图片转Base64字符串.并支持markdown文件打开展示

引用1.0.3版本或以上的Wesky.Net.OpenTools 包 1.0.3 版本提供图片转Base64字符串方案,并提供根据后缀名自动识别Mime类型,合成标准URI 开源项目地址: Gitee:https://gitee.com/dreamer_j/open-tools.git Github

[大数据][机器学习]之Model Card(模型卡片)介绍

每当我们在公有云或者私有云发布训练好的大数据模型,为了方便大家辨识、理解和运用,参照huggingface所制定的标准制作一个Model Card展示页,是种非常好的模型展示和组织形式。 下面就是一个Model Card 的示例,我试着把它翻译成了中文,源网址,并且提供了Markdown的模板,供大

Java 将Markdown文件转换为Word和PDF文档

Markdown 凭借其简洁易用的特性,成为创建和编辑纯文本文档的常用选择。但某些时候我们需要更加精致的展示效果,例如在专业分享文档或打印成离线使用的纸质版时,就需要将Markdown文件以其他固定的文档格式呈现。通过将 Markdown 转换为 Word 和 PDF 格式,可以得到更多的格式设置,

在鼠标右键菜单中新增新建Markdown文件选项(VSCode)

引言 正常情况下,我们新建md文件有两种方式:一是通过Markdown编辑器新建,二是新建txt文件再修改后缀。 但是在Windows系统中,我们可以通过修改注册表来新增右键菜单选项。这里我们可以通过修改注册表来新增新建Markdown文件选项,这样可以减少新建文件的繁琐操作。 下面就来演示在Win

Markdown入门

Markdown基本用法,摘自b站博主。

Typora 显示数学公式

Markdown 数学公式: https://www.cnblogs.com/vipsoft/p/17141603.html $\sum$ ``` $\sum$``` 显示如下:不能正确显示数学公式 解决方案 文件 -> 偏好设置 -> Markdown 扩展语法 -> 全部选中 -> 关闭重新打开