由浅入深:自己动手开发模板引擎——置换型模板引擎(一)
受到群里兄弟們的竭力邀請,老陳終于決定來分享一下.NET下的模板引擎開發(fā)技術(shù)。本系列文章將會帶您由淺入深的全面認(rèn)識模板引擎的概念、設(shè)計(jì)、分析和實(shí)戰(zhàn)應(yīng)用,一步一步的帶您開發(fā)出完全屬于自己的模板引擎。關(guān)于模板引擎的概念,我去年在百度百科上錄入了自己的解釋(請參考:模板引擎)。老陳曾經(jīng)自己開發(fā)了一套網(wǎng)鳥Asp.Net模板引擎,雖然我自己并不樂意去推廣它,但這已經(jīng)無法阻擋群友的喜愛了!
很顯然,置換型模板引擎說的就是替換式模板引擎。它的工作原理是查找和替換字符串,但這個字符串的替換過程又分為直接查找替換和按流替代輸出兩種。直接查找替換實(shí)現(xiàn)簡單,但存在一定的性能障礙,而按流替代的方式性能更好一點(diǎn),但理解起來卻沒那么容易。今天我們就專門來討論討論置換型模板引擎的關(guān)鍵技術(shù)和實(shí)現(xiàn)。
概念
按照不同的置換方案,置換型模板引擎分為查找替換式和按流替代式兩種。查找替換式又分為字符串替換和正則替換兩種。
大家應(yīng)該都很理解查找替換式模板引擎的含義了,它就是把定義好的標(biāo)記替換為我們需要的內(nèi)容即可。而按流替代式雖然在結(jié)果上也是將我們指定的標(biāo)記“替換”為了實(shí)際內(nèi)容,但它內(nèi)部并沒有用到Replace()這種方式,而是在遇到標(biāo)記的時候直接寫出對應(yīng)的內(nèi)容,它實(shí)際上沒有所謂的替換操作,因此性能會好一些。
查找替換式
查找替換這個詞大家已經(jīng)是再也熟悉不過了,我們幾乎每天都要編寫一次或幾次Replace()代碼。而查找替換也是構(gòu)建模板引擎的最基本的思想,任何模板引擎的機(jī)制歸根結(jié)底都還是查找替換。假設(shè)我們有如下模板代碼和業(yè)務(wù)數(shù)據(jù):
1 /// <summary> 2 /// 模板文本。 3 /// </summary> 4 private const string _TEMPLATE_STRING = @"<a href=""{url}"">{title}</a><br />"; 5 6 /// <summary> 7 /// 業(yè)務(wù)數(shù)據(jù)。 8 /// </summary> 9 private Dictionary<string, string> _newsItems = new Dictionary<string, string> { 10 {"http://news.qq.com/a/20120330/001077.htm", "7名塔利班人員喬裝女性 欲打入北約內(nèi)部被捕"}, 11 {"http://news.qq.com/a/20120330/001409.htm", "烏克蘭遭輪奸焚燒少女傷重不治離世"}, 12 {"http://news.qq.com/a/20120330/001288.htm", "國際空間站宇航員拍到北馬里亞納一火山噴發(fā)"}, 13 {"http://news.qq.com/a/20120330/001535.htm", "土耳其同性戀男子可免除兵役 須提供確鑿證據(jù)"}, 14 {"http://news.qq.com/a/20120330/000874.htm", "英首相卡梅倫公布宴客名單 否認(rèn)為籌款聚餐"}, 15 {"http://news.qq.com/a/20120329/001774.htm", "孩子飛行途中胡鬧不守秩序 全家四口被趕下飛機(jī)"}, 16 {"http://news.qq.com/a/20120329/001771.htm", "男子上廁所時抽水馬桶突然爆炸 大腿和背部受傷"}, 17 {"http://news.qq.com/a/20120329/001685.htm", "美國總統(tǒng)奧巴馬有意與林書豪切磋球藝"}, 18 {"http://news.qq.com/a/20120329/001696.htm", "法國家長與教師聯(lián)合抵制家庭作業(yè) 稱加劇不平等"}, 19 {"http://news.qq.com/a/20120329/001650.htm", "印尼政府稱超短裙“色情” 欲對其頒布禁令"} 20 };字符串替換
要使用如上的業(yè)務(wù)數(shù)據(jù)生成一個新聞列表,那么做法不外乎如下的代碼實(shí)現(xiàn)了:
1 /// <summary> 2 /// 最簡單的模板引擎。 3 /// </summary> 4 [Test] 5 public void StringReplace() 6 { 7 var html = new StringBuilder(); 8 9 foreach (var newsItem in this._newsItems) 10 { 11 // 查找替換 12 var news = _TEMPLATE_STRING.Replace("{url}", newsItem.Key).Replace("{title}", newsItem.Value); 13 14 html.AppendLine(news); 15 } 16 17 Trace.WriteLine(html.ToString()); 18 }其輸出結(jié)果如下:
1 <a href="http://news.qq.com/a/20120330/001077.htm">7名塔利班人員喬裝女性 欲打入北約內(nèi)部被捕</a><br /> 2 <a href="http://news.qq.com/a/20120330/001409.htm">烏克蘭遭輪奸焚燒少女傷重不治離世</a><br /> 3 <a href="http://news.qq.com/a/20120330/001288.htm">國際空間站宇航員拍到北馬里亞納一火山噴發(fā)</a><br /> 4 <a href="http://news.qq.com/a/20120330/001535.htm">土耳其同性戀男子可免除兵役 須提供確鑿證據(jù)</a><br /> 5 <a href="http://news.qq.com/a/20120330/000874.htm">英首相卡梅倫公布宴客名單 否認(rèn)為籌款聚餐</a><br /> 6 <a href="http://news.qq.com/a/20120329/001774.htm">孩子飛行途中胡鬧不守秩序 全家四口被趕下飛機(jī)</a><br /> 7 <a href="http://news.qq.com/a/20120329/001771.htm">男子上廁所時抽水馬桶突然爆炸 大腿和背部受傷</a><br /> 8 <a href="http://news.qq.com/a/20120329/001685.htm">美國總統(tǒng)奧巴馬有意與林書豪切磋球藝</a><br /> 9 <a href="http://news.qq.com/a/20120329/001696.htm">法國家長與教師聯(lián)合抵制家庭作業(yè) 稱加劇不平等</a><br /> 10 <a href="http://news.qq.com/a/20120329/001650.htm">印尼政府稱超短裙“色情” 欲對其頒布禁令</a><br />正則表達(dá)式
正則表達(dá)式實(shí)現(xiàn)模板引擎說起來其實(shí)并不合適,大家也應(yīng)該知道其最臭名昭著的就是性能。我們今天只是簡單的提一下這種方案,并不算是真的要用它做一個模板引擎。但正則表達(dá)式技術(shù)在開發(fā)復(fù)雜的模板引擎是還是非常有用的。
FastReplacer
除了基本的字符串替換和正則表達(dá)式之外,這里我再介紹一種更加高效的替換方式——FastReplacer。原文地址:http://www.codeproject.com/Articles/298519/Fast-Token-Replacement-in-Csharp。
FastReplacer通過將字符串中指定格式的標(biāo)記切分為Token,然后再做查找替換,其效率比字符串替換、正則表達(dá)式和StringBuilder要高出許多倍!詳情請看原文介紹。這里我列舉一下它的使用方法:
如果您有打算做一個查找替換式的模板引擎,那么FastReplacer可能會給你帶來意想不到的性能優(yōu)化!
1 /// <summary> 2 /// FastReplacer實(shí)現(xiàn)。 3 /// </summary> 4 [Test] 5 public void FastReplacer() 6 { 7 var html = new StringBuilder(); 8 9 foreach (var newsItem in this._newsItems) 10 { 11 // 您可以通過修改源代碼為FastReplacer增加一個Clear方法 12 // 避免產(chǎn)生多個實(shí)例以提高性能 13 var fs = new FastReplacer("{", "}"); 14 fs.Append(_TEMPLATE_STRING); 15 16 fs.Replace("{url}", newsItem.Key); 17 fs.Replace("{title}", newsItem.Value); 18 19 html.AppendLine(fs.ToString()); 20 } 21 22 Trace.WriteLine(html.ToString()); 23 }以上是簡單的查找替換方式的舉例,因?yàn)椴皇潜疚牡闹攸c(diǎn),也很好理解,也就不多說了(話說——寫博客腫么這么累泥?)。
按流替代式
幾個月前我第一次看到FastReplacer的時候,以為它內(nèi)部用的也是流式替代,但仔細(xì)研究之后發(fā)現(xiàn)不是。那么到底什么是按流替代呢?
我們來看看如下的代碼拆解(老陳所有的文章都是啟發(fā)式的,因此在文字上不會下很大的工夫,偶滴詞匯華麗的很不明顯哇):
<a href="{url}">{title}</a><br />這段代碼其實(shí)可以看做以下代碼的組合(這里以回車換行符隔開了):
1 <a href=" 2 {url} 3 "> 4 {title} 5 </a><br />現(xiàn)在有沒有感覺到眼前一亮呢?我們把這個字符串按照標(biāo)記拆分成了5段,每一個小段,無論長短,我們都理解為Token。Token在流式解析當(dāng)中是一個最基本的元素。
或許,到這里您已經(jīng)看明白了,其實(shí)就是把模板代碼按照一定的規(guī)則拆分成Token流,就類似于.NET內(nèi)置的各種Stream一樣,與字符串最接近的例如StringReader/StringWriter、XmlReader/XmlWriter等。使用.NET做Web開發(fā)的朋友一定對System.Web.HttpResponse再也熟悉不過了,它的Write()就是封裝了一個字符串的流的寫入操作,只不過這個流最終是寫到HTTP網(wǎng)絡(luò)連接上的。
話題轉(zhuǎn)回來,我們要實(shí)現(xiàn)的流式替代就類似于如下過程:
1 // -------------------------------- 2 // 流程 3 // <a href=" 4 // {url} --> 寫為目標(biāo)數(shù)據(jù) 5 // "> 6 // {title} --> 寫為目標(biāo)數(shù)據(jù) 7 // </a><br /> 8 // -------------------------------- 9 // 偽代碼 10 foreach(var token in tokens) 11 { 12 if (token == "{url}") 13 { 14 Write("鏈接地址"); 15 } 16 else (token == "{title}") 17 { 18 Write("新聞標(biāo)題"); 19 } 20 else 21 { 22 // 原原本本的輸出 23 Write(token); 24 } 25 }看完代碼,您要是再不明白什么是流式替代的話,那我真的要哭了!
不過,新的問題產(chǎn)生了——如何把模板代碼變換為Token流呢?不要著急,這是下一節(jié)我們將要講述的內(nèi)容!
小結(jié)及代碼下載
因?yàn)槲业膶懽鲿r間并不多,因此這里采用了單元測試的一些代碼結(jié)構(gòu),不過對大家閱讀和理解不會造成影響。?
本文代碼將與下一篇文章合并提供下載,祝各位工作順利、開心快樂!
總結(jié)
以上是生活随笔為你收集整理的由浅入深:自己动手开发模板引擎——置换型模板引擎(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【python】简单实现一个模板引擎
- 下一篇: 由浅入深:自己动手开发模板引擎——置换型