使用cmark解析Markdown文档

如果在C或者C++项目中解析Markdown,可以使用cmark库。

开发环境

Fedora系统可以直接通过

dnf install cmark-devel

来安装cmark的开发库。

安装之后,就可以使用头文件/usr/include/cmark.h 中的函数进行开发,最后在程序中链接-lcmark即可。

为了简化这一操作,也可以使用pkg-config文件。

需要注意的是,在Fedora系统中,cmark的pkgconfig文件名字是libcmark.pc。即,pkg-config命令需要查询libcmark。

如:

~/$ pkg-config --cflags --libs libcmark
-lcmark

使用方法

cmark使用三个主要的数据结构,分别是cmark_parser、cmark_iter与cmark_node。

其中,cmark_parser用来解析,有一系列与解析相关的函数;cmark_iter用来遍历,可以实现在cmark_node节点间切换;而cmark_node是所有数据节点。

在头文件中,使用注释的方式说明了cmark_parser的使用方法。

443  * ## Parsing
444  *
445  * Simple interface:
446  *
447  *     cmark_node *document = cmark_parse_document("Hello *world*", 13,
448  *                                                 CMARK_OPT_DEFAULT);
449  *
450  * Streaming interface:
451  *
452  *     cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
453  *     FILE *fp = fopen("myfile.md", "rb");
454  *     while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
455  *         cmark_parser_feed(parser, buffer, bytes);
456  *         if (bytes < sizeof(buffer)) {
457  *             break;
458  *         }
459  *     }
460  *     document = cmark_parser_finish(parser);
461  *     cmark_parser_free(parser);
462  

即可以使用cmark_parse_document解析文本内容,输入参数分别是字符串指针与大小。

也可以使用cmark_parser来解析流式数据,方法为先创建一个cmark_parser,之后使用cmark_parser_feed来喂数据,最后调用cmark_parser_finish来指示解析结束,返回解析出来的cmark_node。

cmark_node

cmark的几个主要数据结构定义,并没有在头文件里,而是使用了typedef的别名。如果只是使用cmark,可以不用关心它们的具体定义,只使用头文件中的操作它们的函数即可。(这种信息隐藏,也是一种故意为之的设计,起到了类似C++中的private作用。)

cmark_node的主要属性,就是它的类型,即cmark_node_type,可以通过:

261 /** Returns the type of 'node', or `CMARK_NODE_NONE` on error.
262  */
263 CMARK_EXPORT cmark_node_type cmark_node_get_type(cmark_node *node);
264 
265 /** Like 'cmark_node_get_type', but returns a string representation
266     of the type, or `"<unknown>"`.
267  */
268 CMARK_EXPORT
269 const char *cmark_node_get_type_string(cmark_node *node);

这两个方法,分别获得cmark_node的类型值,与类型的字符串表示。

cmark_node_type是一个枚举值,定义为:

/** ## Node Structure31  */32 33 typedef enum {34   /* Error status */35   CMARK_NODE_NONE,36 37   /* Block */38   CMARK_NODE_DOCUMENT,39   CMARK_NODE_BLOCK_QUOTE,40   CMARK_NODE_LIST,41   CMARK_NODE_ITEM,42   CMARK_NODE_CODE_BLOCK,43   CMARK_NODE_HTML_BLOCK,44   CMARK_NODE_CUSTOM_BLOCK,45   CMARK_NODE_PARAGRAPH,46   CMARK_NODE_HEADING,47   CMARK_NODE_THEMATIC_BREAK,48 49   CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,50   CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,51 52   /* Inline */53   CMARK_NODE_TEXT,54   CMARK_NODE_SOFTBREAK,55   CMARK_NODE_LINEBREAK,56   CMARK_NODE_CODE,57   CMARK_NODE_HTML_INLINE,58   CMARK_NODE_CUSTOM_INLINE,59   CMARK_NODE_EMPH,60   CMARK_NODE_STRONG,61   CMARK_NODE_LINK,62   CMARK_NODE_IMAGE,63 64   CMARK_NODE_FIRST_INLINE = CMARK_NODE_TEXT,65   CMARK_NODE_LAST_INLINE = CMARK_NODE_IMAGE66 } cmark_node_type;

熟悉Markdown的应该很清楚这些类型的意义,不再赘述。

需要注意的是,获取这些node的属性的方法,需要根据不同的类型,才能取得相应的值。

比如如果是一个CMARK_NODE_HEADDING,就可以通过cmark_node_get_heading_level来取得层级。

 /** Returns the heading level of 'node', or 0 if 'node' is not a heading.
283  */
284 CMARK_EXPORT int cmark_node_get_heading_level(cmark_node *node);

通过cmark_node_set_heading_level来设置层级。

290 /** Sets the heading level of 'node', returning 1 on success and 0 on error.
291  */
292 CMARK_EXPORT int cmark_node_set_heading_level(cmark_node *node, int level);

而如果是一个CMARK_NODE_IMAGE或者CMARK_NODE_URL,则可以通过cmark_node_get_title取得图片的标题,或者URL的显示文本。

/** Returns the title of a link or image 'node', or an empty  
353     string if no title is set.  Returns NULL if called on a node  
354     that is not a link or image.  
355  */  
356 CMARK_EXPORT const char *cmark_node_get_title(cmark_node *node);

通过cmark_node_get_url取得实际的链接地址。

341 /** Returns the URL of a link or image 'node', or an empty string
342     if no URL is set.  Returns NULL if called on a node that is
343     not a link or image.
344  */
345 CMARK_EXPORT const char *cmark_node_get_url(cmark_node *node);
346 

渲染

我们除了可以根据cmark_node来做自定义的操作之外,还可以使用cmark库的渲染方法,把Markdown文本渲染成其它文本格式。

比如,渲染成XML:

508 /** Render a 'node' tree as XML.  It is the caller's responsibility  
509  * to free the returned buffer.  
510  */  
511 CMARK_EXPORT  
512 char *cmark_render_xml(cmark_node *root, int options);  

渲染成HTML:

514 /** Render a 'node' tree as an HTML fragment.  It is up to the user  
515  * to add an appropriate header and footer. It is the caller's  
516  * responsibility to free the returned buffer.  
517  */  
518 CMARK_EXPORT  
519 char *cmark_render_html(cmark_node *root, int options);  

渲染成man手册页:

521 /** Render a 'node' tree as a groff man page, without the header.  
522  * It is the caller's responsibility to free the returned buffer.  
523  */  
524 CMARK_EXPORT  
525 char *cmark_render_man(cmark_node *root, int options, int width);  

渲染成commonmark:

527 /** Render a 'node' tree as a commonmark document.  
528  * It is the caller's responsibility to free the returned buffer.  
529  */  
530 CMARK_EXPORT  
531 char *cmark_render_commonmark(cmark_node *root, int options, int width);  

或者渲染成latex:

533 /** Render a 'node' tree as a LaTeX document.  
534  * It is the caller's responsibility to free the returned buffer.  
535  */  
536 CMARK_EXPORT  
537 char *cmark_render_latex(cmark_node *root, int options, int width);

以上这些都是渲染一个节点,即cmark_node,如果只是最简单的把一个Markdown渲染成HTML,还可以不解析,直接使用一个方法:

27 CMARK_EXPORT  
28 char *cmark_markdown_to_html(const char *text, size_t len, int options);

输入参数是一个字符串和长度,输出一个HTML的字符串,编码都是UTF-8。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890875.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 Python Django 的农产品销售系统的研究与实现

大家好&#xff0c;我是stormjun&#xff0c;今天为大家带来的是基于 Python Django 的农产品销售系统的研究与实现。该系统采用 Python 语言 开发&#xff0c;MySql 作为数据库&#xff0c;系统功能完善 &#xff0c;实用性强 &#xff0c;可供大学生实战项目参考使用。 博主介…

Linux隐藏登录和清除历史命令以及其他相关安全操作示例

隐藏登录 ssh -T rootxxx.xxx.xxx.xxx /bin/bash -i 命令拆解-T &#xff1a;告诉ssh客户端&#xff0c;不要分配一个TTY&#xff08;伪终端&#xff09;root &#xff1a;连接用户xxx.xxx.xxx.xxx &#xff1a;连接的服务器ip地址/bin/bash &#xff1a;在远程服务器上启动…

uniapp实现APP、小程序与webview页面间通讯

需求&#xff1a; 1、需要在Uniapp开发的APP或小程序页面嵌入一个H5网页&#xff0c;需要拿到H5给APP传递的数据。 2、并且这个H5是使用vuevant开发的。&#xff08;其实跟使用uniapp开发H5一样&#xff09; 实现步骤&#xff1a; 1、首先需要兼容多端和App端&#xff0c;因…

每天40分玩转Django:Django部署概述

一、Django部署概述 在开发阶段,我们通常使用Django内置的轻量级开发服务器runserver。但在生产环境中,为了应对大量并发请求,需要使用高性能的WSGI服务器,如Gunicorn、uWSGI等。同时还要配置Nginx等Web服务器作为反向代理,实现负载均衡、静态文件处理等。下面是Django部署的整…

Python 爬虫中的反爬策略及详细应对方法

在构建Python爬虫的过程中&#xff0c;网站为了保护自身资源和用户体验&#xff0c;常常会采取一系列反爬策略来限制或阻止自动化程序的访问。了解这些策略对于设计更智能、更合规的爬虫至关重要。以下是详细的反爬措施及其应对方法&#xff1a; 1. User-Agent 检测 策略描述…

FreeSWITCH 简单图形化界面38 - 使用uniapp中使用JsSIP进行音视频呼叫

FreeSWITCH 简单图形化界面38 - 在uniapp中使用JsSIP进行音视频呼叫 0、测试环境1、学习uniapp2、测试代码main.jsutils/render.jsstore/data.jspages/index/index.vuepages.json 3、效果4、难点 0、测试环境 http://myfs.f3322.net:8020/ 用户名&#xff1a;admin&#xff0c…

【蓝桥杯——物联网设计与开发】拓展模块4 - 脉冲模块

目录 一、脉冲模块 &#xff08;1&#xff09;资源介绍 &#x1f505;原理图 &#x1f505;采集原理 &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#xff09;实验现象 二、脉冲模块接口函数封装 三、踩坑日记 &a…

嵌入式硬件杂谈(八)电源的“纹波”到底是什么?

纹波的引入&#xff1a;在我们嵌入式设备中&#xff0c;很多时候电路电源的纹波很敏感&#xff0c;纹波太大会导致系统不工作&#xff0c;因此设计一个纹波很小的电路就是我们的需求了。 电路的纹波是什么&#xff1f; 纹波&#xff08;Ripple&#xff09;是指电源输出中叠加在…

Linux系统之stat命令的基本使用

Linux系统之stat命令的基本使用 一、stat命令 介绍二、stat命令帮助2.1 查询帮助信息2.2 stat命令的帮助解释 三、stat命令的基本使用3.1 查询文件信息3.2 查看文件系统状态3.3 使用格式化输出3.4 以简洁形式打印信息 四、注意事项 一、stat命令 介绍 stat 命令用于显示文件或文…

uniapp开发微信小程序实现获取“我的位置”

1. 创建GetLocation项目 使用HBuilder X创建一个项目GetLocation,使用Vue3。 2. 在腾讯地图开放平台中创建应用 要获取位置,在小程序中需要使用腾讯地图或是高德地图。下面以腾讯地图为例。 (1)打开腾讯地图开放平台官方网址:腾讯位置服务 - 立足生态,连接未来 (2)注册…

华为,新华三,思科网络设备指令

1. 设备信息查看 华为 display version # 查看设备版本信息 display device # 查看设备硬件信息 新华三&#xff08;H3C&#xff09; display version # 查看设备版本信息 display device # 查看设备硬件信息 锐捷 show version …

基于NodeMCU的物联网空调控制系统设计

最终效果 基于NodeMCU的物联网空调控制系统设计 项目介绍 该项目是“物联网实验室监测控制系统设计&#xff08;仿智能家居&#xff09;”项目中的“家电控制设计”中的“空调控制”子项目&#xff0c;最前者还包括“物联网设计”、“环境监测设计”、“门禁系统设计计”和“小…

深入理解 PyTorch 的 view() 函数:以多头注意力机制(Multi-Head Attention)为例 (中英双语)

深入理解 PyTorch 的 view() 函数&#xff1a;以多头注意力机制&#xff08;Multi-Head Attention&#xff09;为例 在深度学习模型的实现中&#xff0c;view() 是 PyTorch 中一个非常常用的张量操作函数&#xff0c;它能够改变张量的形状&#xff08;shape&#xff09;而不改…

easegen将教材批量生成可控ppt课件方案设计

之前客户提出过一个需求&#xff0c;就是希望可以将一本教材&#xff0c;快速的转换为教学ppt&#xff0c;虽然通过人工程序脚本的方式&#xff0c;已经实现了该功能&#xff0c;但是因为没有做到通用&#xff0c;每次都需要修改脚本&#xff0c;无法让客户自行完成所有流程&am…

从安全角度看 SEH 和 VEH

从安全角度看 SEH 和 VEH 异常处理程序是处理程序中不可预见的错误的基本方法之一 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/ SEH——结构化异常处理程序 就其工作方式而言&#xff0c;异常处理程序与其他处理程序相比相当基础&#xff0…

nexus docker安装

#nexus docker 安装 docker pull sonatype/nexus3 mkdir -p /data/nexus-data docker run -itd -p 8081:8081 --privilegedtrue --name nexus3 \ -v /data/nexus-data:/var/nexus-data --restartalways docker.io/sonatype/nexus3 #访问 http://192.168.31.109:8081/ 用户名&am…

华为电源工程师面试题

电源工程师面试题涵盖硬件设计、仿真和验证、问题解决、元件选择、热设计和功耗分析、软件配合以及专业知识等多个方面。以下是一些可能的面试题及其参考回答思路: 一、硬件设计和架构 请解释一下LLC谐振变换器的工作原理以及它的优势。 回答思路:LLC谐振变换器是一种高效、…

Pinia最简单使用(vite+vue3)

文章目录 创建项目安装Pinia包main.js注册Pinia在src下创建store/store.js文件,放入以下内容在app.vue中的使用(在其他组件也一样的) 创建项目 npm create vitelatest my-vue-app选vue 选JavaScript cd my-vue-app npm install npm run dev安装Pinia包 npm install piniamain…

Spark生态圈

Spark 主要用于替代Hadoop中的 MapReduce 计算模型。存储依然可以使用 HDFS&#xff0c;但是中间结果可以存放在内存中&#xff1b;调度可以使用 Spark 内置的&#xff0c;也可以使用更成熟的调度系统 YARN 等。 Spark有完善的生态圈&#xff1a; Spark Core&#xff1a;实现了…

DP之背包基础

目录 DP简介 01背包问题 采药(01背包例题) 完全背包 疯狂的采药(完全背包例题) 背包变式 装箱问题 砝码称重 质数拆分 优化思考 DP简介 全称Dynamic Programming即动态规划 DP算法是解决多阶段决策过程最优化问题的一种常用方法。 多阶段决策过程是指这样一类特…