[ 蓝桥杯Web真题 ]-Markdown 文档解析

news2024/11/18 5:59:26

目录

介绍

准备

目标

规定

思路

补充知识

解法参考


介绍

Markdown 因为其简洁的语法大受欢迎,已经成为大家写博客或文档时必备的技能点,众多博客平台都提倡用户使用 Markdown 语法进行文章书写,然后再发布后,实时的将其转化为常规的 HTML 页面渲染。

本题需要在已提供的基础项目中,使用 Nodejs 实现简易的 Markdown 文档解析器。

准备

开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:

├── docs.md
├── images
│   └── md.jpg
├── index.html
└── js
    ├── index.js
    └── parse.js

其中:

  • index.html 是主页面。
  • images 是图片文件夹。
  • docs.md 是需要解析的 Markdown 文件。
  • js/index.js 是提供的工具脚本,用于快速验证代码结果。
  • js/parse.js 是需要补充的脚本文件。

注意:打开环境后发现缺少项目代码,请手动键入下述命令进行下载:

cd /home/project
wget https://labfile.oss.aliyuncs.com/courses/18213/07.zip && unzip 07.zip && rm 07.zip

目标

在 js/parse.js 中实现几种特定的 Markdown 语法解析,目前初始文件中已实现标题解析(即从 # 前缀转换为 <hn> 标签),请你继续完善该文件 TODO 部分,完成剩余语法解析操作,具体需求如下:

1.对分隔符进行解析,Markdown 中使用 --- (三条及以上的短横线) 作为分隔符,将其解析成为 <hr> 标签:

<!-- Markdown -->
----

<!-- 对应 HTML  -->
<hr>

2.对引用区块进行解析,Markdown 中使用 > 作为前缀,将其解析成为 <blockquote> 标签:

<!-- Markdown  -->
> 引用区块1

> 多级引用区块2
> 多级引用区块2

<!-- 对应 HTML  -->
<blockquote>
  <p>引用区块1</p>
</blockquote>

<blockquote>
  <p>多级引用区块2</p>
  <p>多级引用区块2</p>
</blockquote>

3.对无序列表进行解析,Markdown 中使用 * 或者 - 作为前缀,将其解析成为 <ul> 标签:

<!-- Markdown  -->
* 无序列表
* 无序列表
* 无序列表

或者:
- 无序列表
- 无序列表
- 无序列表

<!-- 对应 HTML  -->
<ul>
  <li>无序列表</li>
  <li>无序列表</li>
  <li>无序列表</li>
</ul>

4.对图片进行解析,Markdown 中使用 ![alt](link) 表示,将其解析成为 <img> 标签:

<!-- Markdown  -->
![图片](./images/md.jpg)

<!-- 对应 HTML  -->
<img src="./images/md.jpg" alt="图片">

5.对文字效果进行解析,比如粗体效果,和行内代码块,将其分别解析成 <b> 和 code 标签:

<!-- Markdown  -->
这是**粗体**的效果文字,这是内嵌的`代码行`

<!-- 对应 HTML  -->
这是<b>粗体</b>的效果文字,这是内嵌的<code>代码行</code>

在验证代码效果时,你可以在终端运行:

node ./js/index.js

程序会将解析的结果输出到 index.html 文件中,然后通过浏览器查看输出的 index.html 是否符合解析要求(注意:程序不会实时的将结果更新到 index.html 文件中,在你的代码变更后,请重新执行上述命令)。

在题目所提供的数据的情况下,完成后的效果如下:

规定

  • 请勿修改 js/parse.js 文件外的任何内容。
  • 请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径、class 名、id 名、图片名等,以免造成无法判题通过。自己先做以下把,传送门

思路

本道题在14届省赛中是倒数第二道题目,还是有一定的难度的。本文的题目表示考查的点是Node.js。但是在做这道题目我们压根就不需要使用到Node.js的知识点,因此这部分的功能题目源码都已经帮我们写好了。它主要是使用到了Node.js中的fs模块来读取md文件,后续对其进行读取到的文本内容通过解析之后渲染到了html文档中。如果对Node.js感兴趣的小伙伴可以看我之前发布的文章。一共6篇,还有一篇案例。

这道题主要是给我们一些规则,让我们通过对应的规则去将代码进行转换。做这道题目首先自己需要对正则有一点的了解。然后需要对字符串或者数组的一些方法熟悉,才能方便处理。同时还需要你会观察上下文的代码,通过它已经提供的代码来对其进行理解,然后编写出自己的代码。

补充知识

JavaScript中的正则表达式(正则规则)是用于匹配字符串模式的工具。它们提供了强大的方式来搜索、替换或提取字符串中的特定部分。以下是一些常见的JS正则表达式规则:

1.字面量表示法:使用斜杠(/)来包裹正则表达式模式,例如:/pattern/flagspattern 是你要匹配的模式,flags 是标志,可以是 i(忽略大小写)、g(全局匹配)、m(多行匹配)等。

2.元字符:元字符是在正则表达式中具有特殊含义的字符,如 ^(匹配开头)、$(匹配结尾)、.(匹配除换行符外的任何字符)、*(匹配前一个元素零次或多次)等。

3.字符类:使用方括号 [ ] 来定义一个字符类,代表匹配其中任何一个字符。比如 [abc] 表示匹配字符 abc

4.量词:量词用于指定匹配元素的数量。常见的量词包括 *(零次或多次匹配)、+(一次或多次匹配)、?(零次或一次匹配)、{n}(匹配 n 次)、{n,}(至少匹配 n 次)、{n,m}(匹配 n 到 m 次)等。

5.捕获组:使用括号 () 可以创建一个捕获组,用于匹配子表达式,并可以在后续操作中引用它。

6.特殊字符转义:在正则表达式中,有些字符具有特殊含义,如果想要匹配这些字符本身,需要使用反斜杠 \ 进行转义,比如 \. 可以匹配 . 字符。

7.预定义模式:如 \d(匹配数字字符)、\w(匹配字母、数字或下划线)、\s(匹配空白字符)等,它们表示常见的字符集合。

8.修饰符:修饰符用于指定匹配规则的标志,比如 i(不区分大小写)、g(全局匹配)、m(多行匹配)等。

匹配数字

const pattern = /\d+/;
console.log(pattern.test("Hello 123")); // 输出 true,匹配到数字
console.log(pattern.test("Hello World")); // 输出 false,未匹配到数字

匹配邮箱

const emailPattern = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
console.log(emailPattern.test("example@mail.com")); // 输出 true,匹配邮箱格式
console.log(emailPattern.test("invalid_email.com")); // 输出 false,不匹配邮箱格式

提取字符串中的数字

const str = "Age: 25, Height: 180cm";
const numberPattern = /\d+/g;
const numbers = str.match(numberPattern);
console.log(numbers); // 输出 ["25", "180"]

替换字符串中的特定内容

const sentence = "Learn JavaScript, it's JavaScript";
const replacePattern = /JavaScript/g;
const replaced = sentence.replace(replacePattern, "JS");
console.log(replaced); // 输出 "Learn JS, it's JS"

检查字符串是否以特定模式开头

const startsWithPattern = /^Start/;
console.log(startsWithPattern.test("Starts with Start")); // 输出 true
console.log(startsWithPattern.test("Does not start")); // 输出 false

解法参考

首先我们需要对分隔符来进行解析。我们先写出其对应的正则表达式,Markdown 中使用 --- (三条及以上的短横线) 作为分隔符。使用正则表示为:this.hr = /-{3,}/。接着我们判断是否符合分隔符,并且对其进行转化函数的编写。

  //是否符合分隔符规范
  isHr(){
    return this.hr.test(this.lineText);
  }
  //解析分隔符
  parseHr(){
    return `<hr>`;
  }

然后在渲染类的 runParser()中编写对应的渲染处理。这部分我们跟上面提供的代码的标题函数一样即可。

 // 分割线渲染
      if (this.parser.isHr()) {
        hasParsed.push(this.parser.parseHr());
        currentLine++;
        continue;
      }

接着我们对引用区块进行解析,同样对其进行判断以及解析。这部分的解析我们分为三个方法,开始标签,中间p标签以及结束标签的解析

  // 解析blockQuote开始标签
  parseStartBlockQuote(){
    return `<blockquote>`;
  }
  // 解析blockQuote结束标签
  parseEndBlockQuote(){
    return `</blockquote>`;
  }

接着我们解析中间的文字,我们使用到了split方法来进行获取到的那一行文字通过>符号来进行分割成字符串数组,如下。接着我们获取下标为1的元素,并使用trim()用于去除字符串中的空格。最后将这部分获取到的文字包裹来一个p标签中。

[ '', ' hello world' ]
[ '', ' 这是区块引用' ]
[ '', ' 和上面的文字在同一个区块' ]
  // 生成blockQuote中的p标签
  parseBlockQuoteP(){
    //split将一个字符串分割成字符串数组
    const temp = this.lineText.split(">");
    //trim()用于去除字符串中的空格
    console.log(temp)
    const content = temp[1].trim();
    // console.log(content)
    return `<p>${content}</p>`;
  }

接下来我们对引用区块进行渲染,首先若匹配到 < 标签我们就先为其加上一个块级的开始标签,同时也加上中间文字的解析,然后我们通过循环,来对现在的currentLine不断往下,然后获取对应的行文本内容,判断若匹配不到 < 标签,我们就为其添加一个块级的结束标签。

      // 块作用区渲染
      if (this.parser.isBlockQuote()){
        hasParsed.push(this.parser.parseStartBlockQuote())
        while(true){ 
          hasParsed.push(this.parser.parseBlockQuoteP())
          currentLine++;
          this.parser.parseLineText(this.getLineText(currentLine));
          if(!this.parser.isBlockQuote()){
            hasParsed.push(this.parser.parseEndBlockQuote())
            break
          }
        }
        continue;
      }

接下来的无序列表进行解析为块级作用域的解析思路是一样的。这里就不做过多的解释。

//是否为无序列表
  isUnorderedList(){
    return this.unorderedList.test(this.lineText);
  }
   // // 解析unorderedList开始标签
   parseStartUnorderedList(){
    return `<ul>`;
  }
  // 解析unorderedList结束标签
  parseEndUnorderedList(){
    return `</ul>`;
  }
  // 生成unorderedList中的li标签
  parseUnorderedListLi(){
    //split将一个字符串分割成字符串数组
    const temp = this.lineText.split(" ");
    //trim()用于去除字符串中的空格
    const content = temp[1].trim();
    // console.log(content)
    return `<li>${content}</li>`;
  }
  //无序列表渲染
       if (this.parser.isUnorderedList()){
        hasParsed.push(this.parser.parseStartUnorderedList())
        while(true){ 
          hasParsed.push(this.parser.parseUnorderedListLi())
          currentLine++;
          this.parser.parseLineText(this.getLineText(currentLine));
          if(!this.parser.isUnorderedList()){
            hasParsed.push(this.parser.parseEndUnorderedList())
            break
          }
        }
        continue;
      }

接着我们对图片进行解析,Markdown 中使用 ![alt](link) 表示,将其解析成为 <img> 标签。这部分也比较简单使用到了slice方法来进行截取对应的图片的路径已经alt属性的值。slice方法我们在第一篇真题讲解中有介绍了,可以去看看。

  //是否为图片
  isImage(){
    return this.image.test(this.lineText);
  }
  // 解析image标签
  parseImage(){
    const src=this.lineText.slice(6,-1);
    const alt=this.lineText.slice(2,4);
    return `<img src="${src}" alt="${alt}">`;
  }
 //图片渲染
      if(this.parser.isImage()){
        hasParsed.push(this.parser.parseImage());
        currentLine++;
        continue;
      }

最后,我们需要对文字效果进行解析,比如粗体效果,和行内代码块,将其分别解析成<b>和code标签。this.strongText 是一个匹配粗体文本的正则表达式。replace 方法会将匹配到的粗体文本替换成函数中返回的内容。参数 match 包含了整个匹配的字符串,而 p1 则是捕获组中捕获到的内容,也就是粗体文本的实际内容。若遇到粗体以及代码块的标识则解析成对应的格式,否则对正常文字进行正常解析。

 //解析文本
  parseText(){
     let temp=this.lineText;
     //若有粗体则对其进行添加标签
     temp=temp.replace(this.strongText,(match,p1)=>{
      return `<b>${p1}</b>`;
    })
     //若遇到代码块则对其进行添加标签
    temp=temp.replace(this.codeLine,(match,p1)=>{
      return `<code>${p1}</code>`;
    })
    return temp;
  };
  //渲染文本
      hasParsed.push(this.parser.parseText());
      currentLine++;
      continue;

好啦!本文就到这里结束了~~~

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

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

相关文章

如何入驻抖音本地生活服务商,门槛太高怎么办?

随着抖音本地生活服务市场的逐渐成熟&#xff0c;越来越多平台开始涉及本地生活服务领域&#xff0c;而本地生活服务商成了一个香窝窝&#xff0c;为了保护用户权益和平台生态&#xff0c;对入驻入驻抖音本地生活服务商的条件及审核也越来越严格&#xff0c;这让很多想成为抖音…

【Flink系列四】Window及Watermark

3.1、window 在 Flink 中 Window 可以将无限流切分成有限流&#xff0c;是处理有限流的核心组件&#xff0c;现在 Flink 中 Window 可以是时间驱动的&#xff08;Time Window&#xff09;&#xff0c;也可以是数据驱动的&#xff08;Count Window&#xff09;。 Flink中的窗口…

CSS特效025:旋转的loading状态

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…

idea安装包下载

idea安装教程 IDEA安装包链接&#xff1a;https://pan.baidu.com/s/15dEPF2hV3WPiFWMwGOsKWQ 提取码&#xff1a;kxl7 有激活部署的文档说明&#xff0c;下载自己看吧&#xff0c; 链接&#xff1a;https://pan.baidu.com/s/11yh8cz0R86Ngl7EJN8_5FA 提取码&#xff1a;mdg6

小知识点——Servlet

Servlet 是什么&#xff1f; Java Servlet 是运行在 Web 服务器或应用服务器上的程序&#xff0c;它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet&#xff0c;您可以收集来自网页表单的用户输入&#xff0c;呈…

2023滨海湾人工智能论坛举办,范向伟代表和鲸科技共同发起成立工业智能算法联盟

人工智能是新一轮科技革命和产业变革的重要驱动力量&#xff0c;算力算法产业也正迎来爆发式增长。12月7日&#xff0c;以“要素融合知识互联”为主题的2023滨海湾人工智能论坛在东莞滨海湾新区举行&#xff0c;本次活动由东莞市人民政府主办&#xff0c;东莞滨海湾新区管委会、…

【android开发-21】android中调用系统摄像头camera拍照和相册的用法详解

1&#xff0c;调用摄像头 在Android中&#xff0c;调用系统摄像头拍照需要使用Intent来启动Camera应用&#xff0c;并在应用中设置相应的权限。下面是一个简单的例子&#xff1a; // 创建一个Intent对象&#xff0c;指定要执行的动作是拍照 Intent intent new Intent(Medi…

循环结构中 break、continue、return 和exit() 的区别

循环结构中 break、continue、return 和exit() 的区别 文章目录 循环结构中 break、continue、return 和exit() 的区别一、break语句二、continue语句三、return 语句四、exit() 函数 说明&#xff1a;本文内容参考牟海军 著《C语言进阶&#xff1a; 重点、难点与疑点解析》&a…

MyBatis中的N+1问题,使用ResultSet来解决,需要存储过程【非常详细】

基础表sql 订单表 CREATE TABLE test_order (order_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 订单id,order_name varchar(255) NOT NULL DEFAULT COMMENT 订单名字,PRIMARY KEY (order_id) ) ENGINEInnoDB AUTO_INCREMENT3 DEFAULT CHARSETutf8mb4 COMMENT订单表;INS…

八个适合女大学生做的赚钱小副业

大学的生活可以说是多姿多彩&#xff0c;既有沉浸在课堂知识中的学习&#xff0c;也有课余时间可以自由支配的自我发展。然而&#xff0c;作为一名女大学生&#xff0c;除了追求优异的学业表现&#xff0c;是否还有其他更加多元化且有意义的方式来充实自己呢&#xff1f; 当然&…

Kubernetes集群安装高可用postgresql

Kubernetes集群安装高可用postgresql Bitnami 提供的 postgresql-ha 解决方案是一个预配置的、高可用的 PostgreSQL 集群配置&#xff0c;通常部署在 Kubernetes 环境中。它使用了一些关键技术和组件来实现数据库的高可用性。&#xff0c;Bitnami postgresql-ha 主要采用以下构…

问题:batchnormal训练单个batch_size就会报错吗

Batch Normalization&#xff08;批标准化&#xff09;是一种深度学习中的正则化技巧&#xff0c;它可以改进网络的训练过程。在训练神经网络时&#xff0c;Batch Normalization可以帮助解决内部协变量偏移&#xff08;Internal Covariate Shift&#xff09;的问题。 在标准的…

库卡LBR_iisy_3_R760协作机器人导入到coppeliasim

1.从库卡官网xpert下载模型 一般载都是这个step文件格式&#xff0c;其他的好像不太好用。coppeliasim导入格式用的是stl,需要用freeCAD打开重新转换一下。下载下来后&#xff0c;很多都是一个整体&#xff0c;在freeCAD导入中&#xff0c;导入选择要不勾选合并。 下载完用CAD …

DHTMLX Scheduler PRO 6.0.5 Crack

功能丰富的 JavaScript调度程序 DHTMLX Scheduler 是一个 JavaScript 日程安排日历&#xff0c;具有 10 个视图和可定制的界面&#xff0c;用于开发任何类型的日程安排应用程序。 DHTMLX JS 调度程序库的主要特性 我们的 JS 调度程序最需要的功能之一是时间轴视图。借助时间轴…

USB Type-C一拖二线缆制作方法

1 实现方法 Figure 1-1 Type-C Socket(母口) Figure 1-2 Type-C Plug(公头) Table 1-1 Type-C Socket Pin连接描述 Type-C Plug连接&#xff0c; 需要做一个一拖二的线&#xff0c;一根的一端是USB&#xff0c; 另外一根的一端是USB转UART&#xff0c; 参考Table 1-2。 Table 1…

c++详解栈

一.什么是栈 堆栈又名栈&#xff08;stack&#xff09;&#xff0c;它是一种运算受限的数据结构&#xff08;线性表&#xff09;&#xff0c;只不过他和数组不同&#xff0c;数组我们可以想象成一个装巧克力的盒子&#xff0c;你想拿一块巧克力&#xff0c;不需要改变其他巧克…

私域运营:12个朋友圈经营模板

做私域运营的各位&#xff0c;想必大家都会烦恼朋友圈要发什么才能保证最高效吧&#xff01; 首先&#xff0c;我们需要明确&#xff0c;朋友圈是什么&#xff1f; 朋友圈是我们打造信任感的地方&#xff0c;也是我们的信息能够及时触达用户的重要渠道。很多人都有一个习惯&a…

java每日一记 —— mybatis的动态数据源切换

动态数据源切换 1.环境初始化2.切换数据源代码3.第二节代码的测试4.用注解的方式进行优化 此代码在jdk11上测试通过&#xff0c;SpringBoot版本为2.7.14 1.环境初始化 1.创建两个库 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- 表结构 DROP TABLE IF EXISTS t_stu; CRE…

电脑录屏软件哪个好?全面对比,为您揭秘

电脑录屏软件成为创作者、教育者以及普通用户的得力工具&#xff0c;用于捕捉屏幕上的精彩瞬间或创作教学内容&#xff0c;可是电脑录屏软件哪个好呢&#xff1f;本文将深入比较三款备受瞩目的电脑录屏软件&#xff0c;通过详细的分步骤介绍&#xff0c;帮助用户了解它们的使用…

【Element-ui】Icon 图标与Button 按钮

文章目录 前言一、Icon 图标1.1 作用1.2 使用方法1.3 图标集合 二、Button 按钮2.1 基础用法2.2 禁用状态2.3 文字按钮2.4 图标按钮2.5 按钮组2.6 加载中2.7 不同尺寸 总结 前言 在前端开发中&#xff0c;界面的设计和交互是至关重要的一部分。一个直观、易用的界面往往离不开…