当前位置: 首页 > news >正文

Markdown标题序号处理工具——用 C 语言实现

文章目录

  • 背景
  • 基本环境
  • 创建文件
  • 复制下面的代码
  • 执行编译
  • 减小 exe 文件的体积
  • 配置环境变量
  • 使用方式

背景

自己写过一个 Typora 主题,使用了 CSS 计数器,实现了在 Typora 中自动显示标题的序号。但这个只是视觉上的,并不是实际的内容。如果需要保留这个序号,那么就可以使用这个工具。

如果想要只依靠 Typora 就实现一键自动排序,一键移除编号,那么可以看这篇文章:非CSS主题的方式实现 Typora 标题自动编码功能 —— 全网首发!

基本环境

只要有 git bash 即可。
创建一个目录,然后在 git bash 中切换到这个目录下。

创建文件

可以使用 touch 命令创建一个 mdrenum.c 文件。文件名随便。

touch mdrenum.c

复制下面的代码

复制下方的代码,粘贴到之前创建的文件中:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

#define MAX_LINE_LENGTH 1024
#define MAX_LEVELS 6

typedef struct {
    int counts[MAX_LEVELS]; // 各级标题的当前计数
    int last_level;         // 上一个处理的标题级别
} HeadingState;

void init_heading_state(HeadingState *state) {
    memset(state->counts, 0, sizeof(state->counts));
    state->last_level = 0;
}

bool is_heading(const char *line, int *level) {
    int i = 0;
    while (line[i] == '#') {
        i++;
    }
    
    if (i > 0 && i <= MAX_LEVELS && (line[i] == ' ' || line[i] == '\t')) {
        *level = i;
        return true;
    }
    
    return false;
}

void extract_title_text(const char *line, int level, char *title_text) {
    const char *start = line + level;
    while (*start == ' ' || *start == '\t') {
        start++;
    }
    
    const char *text_start = start;
    while (*text_start) {
        if (isdigit(*text_start)) {
            while (*text_start && (isdigit(*text_start) || *text_start == '.')) {
                text_start++;
            }
            while (*text_start == ' ') {
                text_start++;
            }
            break;
        } else if (*text_start != ' ') {
            break;
        }
        text_start++;
    }
    
    strcpy(title_text, text_start);
}

void generate_heading_number(HeadingState *state, int level, char *number) {
    if (level < state->last_level) {
        for (int i = level; i < MAX_LEVELS; i++) {
            state->counts[i] = 0;
        }
    }
    
    state->counts[level - 1]++;
    
    number[0] = '\0';
    for (int i = 0; i < level; i++) {
        char temp[16];
        sprintf(temp, "%d.", state->counts[i]);
        strcat(number, temp);
    }
    
    // 确保最后有一个点号
    if (number[strlen(number)-1] != '.') {
        strcat(number, ".");
    }
    
    state->last_level = level;
}

void process_file(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (!file) {
        fprintf(stderr, "Error: Could not open file %s\n", filename);
        exit(1);
    }
    
    FILE *temp_file = tmpfile();
    if (!temp_file) {
        fclose(file);
        fprintf(stderr, "Error: Could not create temporary file\n");
        exit(1);
    }
    
    HeadingState state;
    init_heading_state(&state);
    
    char line[MAX_LINE_LENGTH];
    while (fgets(line, sizeof(line), file)) {
        int level;
        if (is_heading(line, &level)) {
            char title_text[MAX_LINE_LENGTH];
            extract_title_text(line, level, title_text);
            
            char number[MAX_LINE_LENGTH];
            generate_heading_number(&state, level, number);
            
            // 正确格式: 适当数量的# + 空格 + 序号 + 空格 + 标题文本
            // 先写入适当数量的#
            for (int i = 0; i < level; i++) {
                fputc('#', temp_file);
            }
            // 然后写入空格、序号和标题
            fprintf(temp_file, " %s %s", number, title_text);
        } else {
            fputs(line, temp_file);
        }
    }
    
    fclose(file);
    
    file = fopen(filename, "w");
    if (!file) {
        fclose(temp_file);
        fprintf(stderr, "Error: Could not open file %s for writing\n", filename);
        exit(1);
    }
    
    rewind(temp_file);
    while (fgets(line, sizeof(line), temp_file)) {
        fputs(line, file);
    }
    
    fclose(file);
    fclose(temp_file);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <markdown_file.md>\n", argv[0]);
        return 1;
    }
    
    process_file(argv[1]);
    printf("Successfully processed %s\n", argv[1]);
    
    return 0;
}

执行编译

复制下方的命令,到你创建的 C语言文件所在的目录中执行【需要注意文件的名称要一致】:

gcc -Os -o mdrenum.exe mdrenum.c

减小 exe 文件的体积

还是在 git bash 中,执行下方的命令【strip 命令在 git bash 环境中自带了】:

strip --strip-all mdrenum.exe

经过这个 strip 命令 处理后的 exe 文件,体积会大幅度减小。【从59KB 缩减到了 只有 17KB 的大小】。

配置环境变量

可以将编译得到的 exe 文件放到一个特定的目录下,然后将这个 exe 文件配置到系统的环境变量中。这样就可以保证,在终端中的任何目录下,都可以使用这个 mdrenum.exe 文件来处理 markdown 文件的标题序号了。

使用方式

在配置了环境变量后,直接打开任意终端,切换到要处理的 markdown 文件的目录,然后使用:

mdrenum.exe      要处理的md文件名.md

即可。

http://www.dtcms.com/a/122075.html

相关文章:

  • 最新Web系统全面测试指南
  • lab-foundation开源程序AI/数据科学的瑞士军刀,开箱即用的数据科学/AI 平台 |AI/数据科学的瑞士军刀
  • java设计模式-代理模式
  • C语言操作符详解:从基础到进阶
  • Vue3中watch监视ref对象方法详解
  • win10开机启动文件夹所在位置
  • MQTT-Dashboard-数据集成
  • JS 箭头函数
  • 深度了解向量引论
  • 【Linux】——文件(下)
  • 基础环境配置
  • 使用Python的Schedule库实现定时任务,并传递参数给任务函数
  • 【教学类-102-06】蛋糕剪纸图案(留白边、沿线剪)05——Python制作1图2图6图
  • linux kernel arch 目录介绍
  • 函数作为参数传递
  • 人力外包解决方案:重构企业用人成本的最优配置
  • VUE中的pinia
  • 使用切面的权限注解,可以重复修饰同一个接口
  • vue3腾讯云直播 前端拉流(前端页面展示直播)
  • Green-AI-Resources开源程序是用于环境可持续 AI 开发和部署的精选研究、工具和最佳实践集合
  • centos-LLM-生物信息-BioGPT安装
  • RecyclerView 和 ListView从 设计理念、性能优化 和 扩展能力 三个维度展开分析
  • 基于开源 AI 大模型 AI 智能名片 S2B2C 商城小程序的京城首家无人智慧书店创新模式研究
  • 编码常见的 3类 23种设计模式——学习笔记
  • python处理excel文件
  • 127.0.0.1本地环回地址(Loopback Address)
  • LeetCode 相交链表题解:双指针的精妙应用
  • 我的NISP二级之路-04
  • 系统分析师(二)--操作系统
  • CD24.【C++ Dev】类和对象(15)初始化列表(下)和对象隐式类型转换