APP下载

基于Markdown语言的博客生成程序
----程序设计教学研究

2018-12-21吴修庆安徽阜阳技师学院

数码世界 2018年9期
关键词:博客页面标签

吴修庆 安徽阜阳技师学院

1 课题简介

1.1 选题背景:程序员个人博客发展及现状分析

博客(英语:Blog,为Web Log的混成词),意指log on the web意即在网络上纪录,是一种由个人管理、张贴新的文章、图片或视频的网站或在线日记,用来纪录、抒发情感或分享信息。博客上的文章通常根据张贴时间(Chronological Order),以倒序方式由新到旧排列。

作为一名计算机专业教师,帮助学生编写自己的个人技术博客对于激发学生学习兴趣来说是非常重要的。而博客的发展又分为了三个阶段。

第一阶段:一开始大多初学者会选择使用免费的第三方网站提供的博客系统:如新浪博客,博客园,csdn等,很快,程序员就发现这些博客难以备份,系统代码不可见,定制型差,程序员只能使用其内置功能和内置模版。

第二阶段:随着学习的深入,具备一定开发能力开始使用如WordPress,Typecho等开源博客系统,将其架设在自己的个人购买的服务器上。WordPress一度成为主流的博客搭建方法,其架设在PHP服务器上。拓展性好,代码可见,插件众多。但WordPress有个显著的缺点:需要一个后台语言(WordPress需要的是PHP)支持的服务器。很多在读的计算机专业学生没有资金去负担一个个人服务器。

第三阶段:成立于2007年,基于Git分布式管理的全球最大开源项目管理平台Github是当今开源软件业的一杆旗帜。而Github向个人开发者提供了Github Pages这一服务来供个人开发者展示自己的作品。GitHub Pages提供了一个全免费的静态http服务器,支持自制页面和Jekyll。用户可以非常方便的通过Git将自己的http页面上传至Github Pages并绑定自己的顶级域名。Github Pages不支持如PHP、Python的服务端语言,但一个无限空间、访问稳定的http空间已经为我们提供了无限可能。

综合Github Pages的特点,结合教学开发了这套静态博客生成程序。本系统采用了PHP 5作为开发工具,提供了一套完整的静态网页生成程序,学生可以使用本系统为自己写好的Markdown博客生成出全静态的HTML,学生可以通过自定义css和生成html的模版文件自定义自己想要的界面,可以用过自己引入js来自定义自己想要的插件。系统默认提供了一套我个人使用的模版,默认集成了Highlight.js插件用于代码高亮显示,集成了Duoshuo评论系统用于使静态页面也能实现即时页面评论。

目前Github上最著名的这类静态博客软件是Hexo。Hexo使用起来非常繁琐,需要Node.js的支持,全字符界面,修改比较困难在教学中使用了PHP作为开发工具,相对于Hexo更为成熟简便,学生容易实现。

1.2 课题的意义

结合多年的程序设计课堂教学,发现程序设计课堂教学存在几个教学切入点,一方面:学生需要切合生活实际的操作性强的教学案例;另一方面:Markdown语言有助于初学者对程序设计课程的理解;除此之外:博客的建立使学生有一个自我展示的载体从而获得较强专业成就感。常规教学案例中提供的第三方免费服务使博客文章难以备份,系统代码不可见,定制型差,学生练习只能使用其内置功能和内置模版;而WordPress, Typecho等第三方需要服务端语言支持的博客固然好用,但服务器租用是一笔不小的支出。

因此,本文可以帮助同学利用所学知识自己动手开发一套静态博客,用于记录自己的学习历程,分享技术成果,提高自我专业认可度。在下面的各章中我将详细的阐述开发过程和开发中遇到的问题及解决方法。

2 可行性研究

可行性研究的目的是用最小的代价,在尽可能短的时间内确定问题是否能够解决,它的目的不是解决问题,而是确定问题是否值得去解决,结合课堂教学实际可行性主要从以下四个方面来考虑:

2.1 技术可行性

静态博客生成程序是要求网页将本来后台语言分离出来提前生成好。本系统使用PHP 5.3实现,默认界面模版使用了Bootstrap框架加上手动的HTML,CSS编写。结合专业开设的多种程序设计语言,并且经常在课堂教学中贯穿软件开发小项目,使学生对系统开发有一定的了解,再加指导老师的指导,和详细的参考文档,所以在技术上是可行的。

2.2 操作可行性

运行可行性主要是分析操作学生是否具有开发和运行维护软件开发注册系统的能力。

作为一个计算机或相关专业的学生,必定接触过服务器,编写Markdown格式语言的博客也容易掌握。同时,本软件的计算机的软硬件条件运行系统的条很低件。用户对系统的更新维护具备足够的管理能力。

所以,本系统用于教学直观易懂,有助于学生软件开发实践。

3 需求分析

3.1 软件需要解决的主要问题

本教学案例中生成出的是一套静态博客系统,全部页面均为html,所以一些正常博客很容易实现的数据关系在本软件中难以通过原有方式实现。如通过标签分类博客文章,通常博客系统会使用数据库关联表,对每篇文章的每个标签获取视图。本软件需要另辟蹊径去解决这一问题。

同时,Markdown自身语法并没有时间戳、标题、标签等标记功能。本软件在开发过程中,我首先考虑使用json,用户在写博客的同时写一个里面含有文章标题、写作日期、标签、简介的json文件。后来考虑到这么实现过于繁琐,故抛弃。软件要实现在Markdown文件内部标志这些信息,同时读取时过滤这些信息至内容数组。

还有,一个正常的博客肯定会有评论功能。本软件生成的静态博客,本身不可能带有数据库,我们需要在这个前提下实现评论系统。

3.2 系统具备的基本功能

静态博客生成程序应具备以下功能:

1.对Markdown文档进行分析转换

系统是基于Markdown这一通用标记型语言写成的文档进行转换的。系统首先需要一个从Markdown文档生成HTML标签的转换器。

2.对文档进行归纳

将所有Markdown文档转换后,系统需要对所有文档的信息进行总结归纳,生成归档页面。再提取出所有标签,通过标签文档的对应关系生成标签检索页面。

3. 自定义博客模板本软件不针对个人,所以需要拥有很强的自定义性。系统生成HTML的模板开放给用户可编辑。同时CSS和其他插件用户根据需要也可以自行修改。

4 总体设计

4.1 概述

本阶段设计的基本目标是解决系统如何实现问题,也叫做概要设计,本阶段主要任务是划分出系统的物理元素及设计软件的结构,完成软件定义时期的任务之后就应该对系统进行总体设计,即根据系统分析产生的分析结果来确定这个系统由哪些系统和模块组成,这些系统和模块又如何有机的结合在一起,每个模块的功能如何实现。系统设计的目标是使系统实现拥有所要求的功能,同时,力争达到高效率、高可靠性、可修改性,并且容易掌握和使用。

模块化的依据是:把复杂问题分解成许多容易解决的小问题。原来的问题也就变得容易解决。模块化设计是把大型软件按照一定的原则划分成一个较小的相对功能独立又相关联的模块。每个模块完成一个特定的子功能。把这些模块结合起来组成一个整体。完成指定的功能,满足问题的要求。采用模块化原理的优点在于可以使软件结构清晰,容易测试和调试。从而提高软件的可靠性,可修改性。有助于软件开发的组织管理。一个大型软件可分别编写不同的模块。程序设计课程教学中提到模块化理论的几个重要概念如下:

(1) 抽象

抽象就是抽象出事物的本质特性而暂时不考虑它们的细节。处理复杂系统唯一有效的方法是用层次的方式构造和分析它。一个复杂的动态系统首先可以用一些高级的抽象概念构造和理解,这些高级概念又可以用一些较低级的理解,直到最低层次的具体元素。

(2) 信息隐蔽和局部化

信息隐蔽是指在设计和确定模块时,应使得一个模块内包含的信息对于不需要这些信息的模块来说,是不能访问。

局部化是指把一些关系密切的软件元素物理的放得彼此靠近。局部化有助于实现信息隐蔽。

信息隐蔽原理和局部化有助于在测试期间以及软件维护期间修改软件。因为绝大多数数据和过程对于软件的其它部分而言是隐蔽的,从而由疏忽引入的错误就很少可能传播到软件的其它部分。

(3) 逐步求精的模块化概念

逐步求精和模块化的抽象是密切相关的。软件结构每一层中模块表示对软件抽象层次的次细化。用自顶向下,逐步求精的方法由抽象到具体的方式分配控制,简化了软件设计和实施,提高了软件的可理解性和可测试性,并使得软件更容易维护。

(4) 模块独立性

模块的划分要使模块间尽可能的相互独立,独立模块较易维护。度量模块的独立程度有两个标准:内聚和耦合。耦合是对一个软件结构内不同模块之间互连程度的度量。耦合强弱取决于模块间接口的复杂程度,进入或访问一个模块的点,以及通过接口的数。

在软件的设计中应追求尽可能松散的耦合。内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展,理想内聚的模块只做一件事情。在设计时应力求做到高内聚。

4.2 系统功能划分

4.2.1 博客构建系统功能划分

博客构建系统完成分析Markdown文件和根据信息调用模版生成HTML代码的功能,开发者系统包括的主要功能模块有读取Md文件、构建内容数组、构建标签数组、生成HTML等。

4.2.2 页面模版划分

页面模版包括了一些页面的制作。主要有首页模板、标签模板、页面模板和第三方开源库。

4.3 系统功能描述

4.3.1 博客构建系统功能描述

博客构建系统包含以下4个重点功能。

1.读取Markdown文件

读取Markdown文件使用readfile.php中定义的readMdFile函数完成。项目一开始使用了PHP Sundown这一开源项目,Sundown是最完善,发展时间最长的一个Markdown转HTML的项目。PHP Sundown是Sundown的一个PHP封装。但PHP Sundown需要以php extension形式安装,用户需要安装php后再去github下载PHP Sundown的源代码,自行Make再安装。给本软件的用户带来了不小的麻烦。我最后改用了PHP-Markdown,并自行修改了一些代码方便更适合本项目使用。同样的项目还有Parsedown等作品。

2.构建内容数组

内容数组的格式为$content[$i][’key’],$i表示文章 id,对于每个$content[$i],都是第$i篇文章对应的关联数组。键包含:

$content[$i][‘postbody’] :正文

$content[$i][‘title’] :标题

$content[$i][‘datetime’] :文章创建时间

$content[$i][‘tags’] :用于直接在页面显示的标签字符串

$content[$i][‘tagarray’] :用于后期生成tag页面的当前页面标签数组

$content[$i][‘contentnext’] :对应的下一篇文章

$$content[$i][‘contentpre’] : 对应的上一篇文章

其中,Markdown文件中包含了前五个,contentnext和contentpre在scanfolder.php中对content数组按文章创建时间排序后设置。

3.构建标签数组

在scanfolder中结束对每个Markdown文件执行readMdfile函数后,我们得到的标签只存在于每篇文章的$content[$i][‘tagarray’]中。我们现在需要反向生产一个标签->含有这个标签的页面的关联数组。

4.生成HTML

博客构建系统的最后一个功能也是最重要的功能就是HTML的生成。系统在生成HTML前会包含对应的模板文件,使用str_replace函数将模板中预先设定好要替换的部分替换为内容数组中对应的部分,再将HTML对应的生成在out文件夹下。

同时,生成HTML时对应的CSS和Javascript文件不可缺少,系统内包含了一个我写的xCopy函数,递归的将子文件夹下所有CSS,js文件复制入out文件夹内。

本系统在开发时使用了Bootstrap前端框架。模板使用了Bootstrap默认布局和一些按钮样式。在页面模板中还使用了Bootstrap提供的一些图标。同时,页面模板中代码部分使用Highlight.js进行代码高亮。在页面尾部使用了多说评论系统。

5 详细设计与系统实施

5.1 设计方法及使用算法

详细设计阶段的根本目标是确定应该怎样具体地实现所要求的系统,也就是说,经过这个阶段的设计工作,应该得出目标系统的精确描述,从而在编码阶段可以把这个描述直接翻译成用某种程序设计语言书写的程序。

详细设计的目标不仅仅是逻辑上正确地实现每个模块的功能,更重要的是设计的处理过程应该尽可能简明易懂,详细设计阶段的任务还不是具体的编写程序,而是要设计出程序的“蓝图”,以后根据这个蓝图编写出实际的程序代码。

5.1.1 Markdown 文件信息分析的实现

Markdown语言有自己的语法定义,简要如下:

Markdown语言要素一般分为标题、粗体斜体、分割线、列表、超链接、图片、代码

以下着重讲解代码

语法:Markdown里语法只需要缩进4个空格或者1个Tab

示例:Normal Text Code Text Normal Text 对应Html中标签

综上,程序在将Markdown转换成HTML时只需根据Markdown语法对应的HTML标签进行生成。同时,我们需要在Markdown文件中包含博客信息。程序中定义的Markdown头部写法如下:title: 文章标题,date: 文章时间,格式YYYY-MM-DD HH:MM:SS,tags: - 标签1- 标签 2,description: 简介(用于显示在首页)

这样,程序需要在调用分析Markdown语法函数之前先逐行读入源文件,匹配title、date等信息,直到读入‘---时一并读入源文件剩余全部内容,并对剩余内容调用Markdown分析器。源代码(readfile.php):

//读取分析markdown文件

function readMdFile($sFilename,&$array) {

$sMdFileDirName = "post/".$sFilename.".md";$array['ta gs'] = "";

$a r r ay['t a g s a r r ay'] = a r r ay();$h a nd le =fopen($sMdFileDirName,"r");

while (!feof($handle)) {$line = fgets($handle);

if (trim($line) == "---") {$sOriMdText =fread($handle,800000);

$array['postbody'] = Markdown::defaultTransform($sOri MdText);}

else {$strinfo = trim($line);$strlength = strlen($strinfo);

if (substr($strinfo,0,6)=="title:") {$array['pagetitle'] = su bstr($strinfo,6,$strlength-6);}if (substr($strinfo,0,5)=="date:"){

$a r r a y['d a t e t i m e'] = s u b s t r($s t r i n f o,5,$strlength-5);}if (substr($strinfo,0,5)=="tags:") { }i f(substr($strinfo,0,2)=="- ") {

$taginfo = substr($strinfo,2,$strlength-2);

$array['tags'] = $array['tags']. ' '; array_push($array['tags array'],$taginfo);}

if (substr($strinfo,0,12)=="description:") {

$d e s = s u b s t r($s t r i n f o,1 2,$s t r l e n g t h-12);$array['description'] = $des;}}}

5.1.2 根据文档创建时间对内容数据排序的实现原理

排序是程序设计中重要知识点,排序系统选用了简单的冒泡排序,冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名。

在scanfolder.php中实现如下:

//内容数组根据文章创建时间排序使用的交换函数

function swap(&$array1,&$array2) {

$arraytemp = $array1;$array1 = $array2;$array2 =$arraytemp;}

for ($i=0;$i<$cnt;$i++) {for ($j=$i+1;$j<$cnt;$j++) {

$datei = $content[$i]['datetime'];$datej = $content[$j]['datetime'];

if (strtotime($datei) > strtotime($datej)) swap($content[$i],$content[$j]);}}

5.1.3 生成HTML时替换模板的实现原理

在项目中,引导同学尝试了两种方法进行模板的预留字符串替换。一种是模板字符串中直接使用$变量名标志需要替换的部分(实现于首页模板和关于/友情链接模板),PHP中,用双引号括起来的字符串中的$变量名会自动替换为$变量名的值。所以我们可以直接先将$变量名从内容数组中赋值好,然后再用require引入模板,模板中标识的变量名就会自动替换为值。

第二种是模板中用’@变量名’去标识,在使用时先导入模板字符串,然后使用php内置函数str_replace。

str_replace的模型是mixed str_replace(mixed $search,mixed $replace),将’@变量名’直接替换为值。

5.2 博客构建系统的实现

博客构建系统的入口文件是scanfolder.php,用户只需要将Markdown文件放在Post目录下,运行scanfolder.php,就可以在out文件夹下生成整套博客。用户可以将out文件夹重命名并拷贝至任何地方,都可以直接运行。

5.2.1 文件复制的实现

在程序开始生成html之前,系统需要将模板所需的CSS和Javascript文件拷贝至out文件夹下给生成的html提供方便携带的CSS/Javascript。PHP自身支持的copy函数只能支持单个文件的拷贝,案例中写了一个xCopy函数,用于递归拷贝一个文件夹内所有文件和子文件夹里所有文件。代码如下所示:

function xCopy($source, $destination, $child){

if (!is_dir($destination)) mkdir ($destination);$handle=di r($source);

while($entry=$handle->read()) {if(($entry!=".")&&($ent ry!="..")){

if(is_dir($source."/".$entry)){if ($child)

xCopy($source."/".$entry,$destination."/".$entry,$child);}else{

copy($source."/".$entry,$destination."/".$entry);}}}return 1;}

5.2.2 目录扫描的实现

程序需要扫描整个post目录,并将目录下除了about.md(用于生成关于页面)和links.md(用于生成友情链接页面)的所有markdown文件筛选出来,这部分代码如下:

$dir = './post'; if (is_dir($dir)) { $handle =opendir($dir);while (($file = readdir($handle)) !== false) {if(pathinfo($file, PATHINFO_EXTENSION) == 'md'

&& basename($file,'.md')!= 'about' && basename($file,'.md')!='links') {

r e a d M d F i l e(b a s e n a m e($f i l e,".md"),$content[$cnt]);$cnt++;}}closedir($handle);}

5.2.3 博客文章页面的实现

博客文章页面需要套用pagemodule://scanfolder.php//循环生成每篇文章的html

for ($i=0;$i<$cnt;$i++) {$pagetitle = $content[$i]['pagetitle'];$datetime = $content[$i]['datetime'];$tags =$content[$i]['tags'];$postbody = $content[$i]['postbody'];

$contentpre = $content[$i]['contentpre'];$contentnext =$content[$i]['contentnext'];

$titlepre = $content[$i]['titlepre'];$titlenext = $content[$i]['titlenext'];

//$key = $i+1;//$url = $i.'.html';$thispagetagcnt =count($content[$i]['tagsarray']);

//e c h o $t h i s p a g e t a g c n t;f o r($j=0;$j<$thispagetagcnt;$j++) {

$tagname = $content[$i]['tagsarray'][$j];if (!array_key_exists($tagname,$tagarray)) {

$tagcnt++;$tagarray["$tagname"] = array();}array_push($tagarray["$tagname"],$i);}

require('pagemodule.php');$sHtmlDirName = "out/".(string)($i+1).".html";

$strout = fopen($sHtmlDirName,"w");fwrite($strout,$str);fclose($strout);//echo $str;}

//pagemodule.php<!DOCTYPE html>

$datetime

$tags

  • 博客页面标签
    刷新生活的页面
    答案
    让Word同时拥有横向页和纵向页
    无惧标签 Alfa Romeo Giulia 200HP
    不害怕撕掉标签的人,都活出了真正的漂亮
    让衣柜摆脱“杂乱无章”的标签
    博客天下
    科学家的标签
    博客相册
    博客相册