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