2017-08-10 by Liuqingwen | Tags: Hexo Web | Hits
使用 Hexo 搭建博客确实简单又强大,简单在于构建和发布过程,强大在于它的扩展性。关于 Hexo 博客插件功能有兴趣的朋友可以参考我之前的一篇文章:分享几个实用的 HEXO 博客功能插件 ,但是有时候这些功能比较官方,我们还是需要自己动手 DIY 一下才能更好的适应自己的网页。我现在使用的博客 RSS 订阅功能这个插件( hexo-generator-feed )就不太适合我自己的博客行情。
问题是这样的,因为我使用了图片懒加载的功能,导致生成的 RSS.xml 文件包含的图片部分是真实地址,部分是预加载图片的地址而不是真实源图片地址:
<p><img src="http://url/to/imgloader.gif" data-echo="real-image.jpg"></p>
<p><img src="real-image.jpg"></p>
<p><img src="http://url/to/real-image.jpg"></p>这个时候就需要自己动手稍微 Hack 一下插件的源码了,对整篇的文字进行查找替换就需要正则表达式派上用场了。
对于 JavaScript 编程我是门外汉,不过好在正则表达式在不同语言之间是通用的,至少大部分场景是这样,那么对于会 Java 的我来说对源码简单修改一下足够了。关于正则表达式这里有一篇文章总结的比较好,刚好介绍了我需要使用的知识点:正则表达式中的不匹配,下面引用的是文章的正则表达式定义表格:
表达式  | 定义  | 表达式  | 定义  | 表达式  | 定义  | 表达式  | 定义  | 表达式  | 定义  | 
|---|---|---|---|---|---|---|---|---|---|
abc  | a或b或c  | .  | 任意单个字符  | a?  | 零个或一个a  | ^abc  | 任意不是abc的字符  | \s  | 空格  | 
a*  | 零个或多个a  | a-z  | a-z的任意字符  | \S  | 非空格  | a+  | 一个或多个a  | a-zA-Z  | a-z或A-Z  | 
\d  | 任意数字  | a{n}  | 正好出现n次a  | ^  | 一行开头  | \D  | 任意非数字  | a{n,}  | 至少出现n次a  | 
$  | 一行末尾  | \w  | 任意字母数字或下划线  | a{n,m}  | 出现n-m次a  | (…)  | 括号用于分组  | \W  | 任意非字母数字或下划线  | 
a*?  | 零个或多个a(非贪婪)  | (a|b)  | a或b  | (a)…\1  | 引用分组  | (?=a)  | 前面有a  | (?!a)  | 前面没有a  | 
对于上面的代码我要做到三点:
src="http://url/to/real-image.jpg"src="real-image.jpg" 改成 src="http://url/to/real-image.jpg"src="http://url/to/imgloader.gif" data-echo="real-image.jpg" 改成 src="http://url/to/real-image.jpg"第三种情况很好处理,正则表达式: /(http\:\/\/url\/to\/imgloader.gif" data-echo=")/g 来进行替换即可 ,这里很多符号需要使用 \ 反斜杠来转义,另外 g 表示全局搜索替换。
第二种情况和第一种情况很相似,但是第一种情况是不需要做任何修改的,刚开始我简单的替换 src=" 为绝对路径 src=http://url/to/ 是行不通的,这样会把第一种情况的图片地址也替换掉: src="http://url/to/http://url/to/real-image.jpg" 这是我不想要的结果!
所以,这里需要用到正则表达式中的不匹配原则了,如果路径中不包含 http:// 那么就是相对地址,需要修改!正则表达式是: /<img src="(?!http:\/\/).+(.jpg|.png|.gif)"/gi ,显然, (?!http:\/\/) 是表示匹配字符串不包含 http:// 的意思,这里注意 i 表示不区分大小写进行搜索, . 表示匹配任何换行符之外的单个字符,然后 + 代表不止一个, (.jpg|.png|.gif) 表示这三种图片格式中的任何一种即可。这样正则表达式就达到匹配搜素的目的了。
另外,正则表达中括号 () 非常有用( (x) 和 (?:x) 含义相反,可以参考相关资料 ),初学者很容易忽略这一点!它的含义和用途是:
(x) 匹配
x并且记住匹配项,就像下面的例子展示的那样。括号被称为捕获括号。 模式/(foo) (bar) \1 \2/中的(foo)和(bar)匹配并记住字符串foo bar foo bar中前两个单词。模式中的\1和\2匹配字符串的后两个单词。注意\1、\2、\n是用在正则表达式的匹配环节。在正则表达式的替换环节,则要使用像$1、$2、$n这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')。
所以最终我的代码如下,我加了两个括号用于记住匹配项并用 $1 和 $2 来使用,代码一目了然:
if(feedConfig.replaceURL) {
    var regLazy = /(http\:\/\/liuqingwen.me\/blog\/images\/imgloader.gif" data-echo=")/g;
    var regSrc = /(<img src=")((?!http:\/\/).+(.jpg|.png|.gif)")/gi;
    posts.forEach(function(post) {
        var coverdiv = post.permalink + post.cover_index;
        var contenthead = '<span class="image main"><img src="' + coverdiv + '" alt="' + post.title + '"></span>';
        var content = post.content.replace(regSrc, "$1" + post.permalink + "$2");
        content = content.replace(regLazy, post.permalink);
        //element.content = contenthead + content;
        post.newContent = contenthead + content;
    });
}注意上面代码中我所注释的那段代码,我发现我并不能直接修改 element.content 那样会导致我所有博客文章和 RSS 文件一同被莫名其妙地改掉,这是我没有预料到的,所以,鉴于 JavaScript 的动态语言特性,我给每篇文章 post 动态地添加了一个属性: post.newContent 用于 RSS 的生成。
最后还需要在模板代码中进行应用:
{% if config.feed.content and post.content and post.newContent %}
    <content type="html"><![CDATA[{{ post.newContent | safe }}]]></content>
{% elif config.feed.content and post.content %}
    <content type="html"><![CDATA[{{ post.content | safe }}]]></content>
{% endif %}其实我们在进行字符串匹配、替换、修改的时候,我们不一定完全需要使用正则表达式,特别是那些不复杂的情况,简单使用字符串的一些标准方法就可以进行查找替换修改了。但是,我觉得能用正则表达式就尽量使用正则表达式,有时候性能也不会差,我给出三点简单的原因:
在对于长篇的文字匹配搜索的时候,正则表达式表达更加合理,速度也不慢,我觉得优先使用正则表达式。虽然我也没有理论支持,但是想想,正则表达式为啥存在于各种语言之中?是吧。

这个函数表面上和 replace 一样,实际上它的第一个参数是一个正则表达式而非字符,所以 "1.2.3".replaceAll(".", "-") 的结果不是 1-2-3 而是 ----- ,因为 "." 是正则表达式代表任何非空字符的匹配规则啊。  

不一定是 JavaScript ,对于 Java 或者其他语言都能通用正则表达式,看来学习它是很有必要的,你说呢?
参考资料:
正则表达式(MDN - Mozilla Developer Network): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
正则表达式中的不匹配: http://www.isnowfy.com/regular-expression-negative/
EJS (GitHub): https://github.com/tj/ejs
SWIG (GitHub): https://github.com/paularmstrong/swig
PUG (GitHub): https://github.com/pugjs/pug