目录
前言
你好,我是喵喵侠。作为一名技术创作者,Markdown我每次写文章都会用到,它可以很方便的帮助我书写文章,让我专注于内容,不需要刻意注重排版。
当一篇文章的目录过多时,往往分不清最终显示的标题,到底是几级标题,以及这些标题是怎么样的层级和排序。
今天我将为你带来一个Markdown加序号的实现方法,看完你立马就能用上。
需求分析
我们这里用Node.js做演示,首先肯定是要读取到md文件内容,找出里面所有的标题,然后在每个标题的后面,添加合适的序号。
这里还需要追加限定条件:
如果最高层级的标题是一级标题#,那么得到的结果是# 1.;
如果最高层级的标题是二级标题##,那么得到的结果是## 1.;
依次类推……
那么怎么实现这样的操作呢,来看下面的实现步骤吧。
实现步骤
读取Markdown文件
从文件系统读取Markdown文件的内容。
解析并确定最高标题级别
遍历文件内容,确定最高标题级别。
解析并处理每一行
根据最高标题级别,逐行检查是否为标题行,更新计数器。
维护标题级别的计数器
使用一个数组记录每个级别标题的当前计数,并根据标题级别进行更新。
构造新的标题行
根据计数器的值构造新的标题行,添加相应的序号。
写回文件
将处理后的内容重新写回文件。
实现代码
废话不多数,直接上代码!
const fs = require('fs'); const path = require('path'); // 读取Markdown文件 const filePath = path.join(__dirname, 'your-file.md'); let fileContent = fs.readFileSync(filePath, 'utf8'); // 分割文件内容为行 let lines = fileContent.split('\n'); // 确定最高标题级别 let highestHeadingLevel = 6; // 初始化为最大标题级别 lines.forEach(line => { if (line.startsWith('#')) { const headingLevel = line.split(' ')[0].length; if (headingLevel < highestHeadingLevel) { highestHeadingLevel = headingLevel; } } }); // 初始化计数器数组,根据最高标题级别决定长度 let headingCounters = Array(6 - highestHeadingLevel + 1).fill(0); // 处理每一行 lines = lines.map(line => { if (line.startsWith('#')) { const headingLevel = line.split(' ')[0].length; if (headingLevel >= highestHeadingLevel) { const index = headingLevel - highestHeadingLevel; headingCounters[index] += 1; // 当前级别计数加1 // 将当前级别以下的计数器归零 for (let i = index + 1; i < headingCounters.length; i++) { headingCounters[i] = 0; } // 生成序号前缀 const headingNumber = headingCounters.slice(0, index + 1).join('.'); // 重构标题行 line = line.replace(/^#+/, match => `${match} ${headingNumber}.`); } } return line; }); // 写回Markdown文件 fileContent = lines.join('\n'); fs.writeFileSync(filePath, fileContent, 'utf8'); console.log('标题已添加序号');
代码解析
乍一看上面代码,可能读不太明白,这里做一个解析。
读取Markdown文件
读取Markdown文件的内容。
const filePath = path.join(__dirname, 'your-file.md'); let fileContent = fs.readFileSync(filePath, 'utf8');
分割文件内容为行
将文件内容按行分割成一个数组。
let lines = fileContent.split('\n');
确定最高标题级别
遍历每一行,确定文件中最高的标题级别(例如,最高标题级别是#还是##)。
let highestHeadingLevel = 6; lines.forEach(line => { if (line.startsWith('#')) { const headingLevel = line.split(' ')[0].length; if (headingLevel < highestHeadingLevel) { highestHeadingLevel = headingLevel; } } });
初始化计数器数组
根据最高标题级别,初始化计数器数组。
let headingCounters = Array(6 - highestHeadingLevel + 1).fill(0);
处理每一行
根据标题级别更新计数器,重构标题行。
lines = lines.map(line => { if (line.startsWith('#')) { const headingLevel = line.split(' ')[0].length; if (headingLevel >= highestHeadingLevel) { const index = headingLevel - highestHeadingLevel; headingCounters[index] += 1; for (let i = index + 1; i < headingCounters.length; i++) { headingCounters[i] = 0; } const headingNumber = headingCounters.slice(0, index + 1).join('.'); line = line.replace(/^#+/, match => `${match} ${headingNumber}.`); } } return line; });
写回文件
将处理后的内容写回Markdown文件。
fileContent = lines.join('\n'); fs.writeFileSync(filePath, fileContent, 'utf8');
结语
本文的代码脚本,会读取Markdown文件,解析并处理每一行标题,根据最高标题级别生成序号,并将处理后的内容写回文件,从而实现标题序号的自动添加。这个代码可以满足基本的使用,还有一些可以优化的点,比方说执行脚本可以输入文件路径、排除部分大标题(比如目录)的序号等等,也可以根据个人需要,改写成网页版本。如果你也喜欢用Markdown写作,不妨试一试吧。