tag:blogger.com,1999:blog-89628670409464267622024-03-13T12:16:09.714+08:00Dino's Sandbox神啊!我也想請祢多給我一些時間啊!<br>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.comBlogger23125tag:blogger.com,1999:blog-8962867040946426762.post-55876583649658451182013-08-02T22:15:00.000+08:002013-08-05T22:30:22.003+08:00追尋適合自己使用的圖形處理 API<h3>A Simple C# Image Process API with Fluent Interface</h3>
<p>哈囉大家,話說這一陣子寫程式數量實在太多,寫得很膩,所以跑來寫篇技術短文,稍微轉移一下注意力。(疑,會有幫助嗎?)</p>
<p>這次分享一下自製的圖形處理庫,實作成 Fluent API,本來自己的程式庫用的不是 Fluent 介面,而是一般的 static method(s),但是過去一段時間被一些圖片處理循序呼叫組合搞得很煩,想說既然這樣一不做二不休來個改版,而且這次還包含了解決自己長久以來的疑惑<strong>「控制 .NET 圖形處理後存檔 JPEG 的品質問題」</strong>,這次重寫也證明了我原來的想法是錯的,並非無解。</p>
<ol>
<li>做成 Fluent API
<p>流利的 API? 哈哈,這名字聽起來就蠻爽的,做起來才會開心啊,如今我們早已習慣這種形式的 API,舉凡前端 jQuery、後端 LINQ、諸多 ORM framework 中都很容易見到這樣的設計,Dino 自己還蠻喜歡的,還是秉持著先構想用戶端使用介面的精神(用戶當然還是我自己 XD),習慣上還是先假想一下,API 具體來說我想要做成什麼樣子</p>
<a name='more'></a>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">
ImageProcess
// 開啟圖片處理
.Handle( Stream | Bitmap | FilePath )
// 圖片處理
.Crop( X, Y, Width, Height )
.Resize( Percentage, Side.Width | Side.Height )
.Resize( Length, Side.Width | Side.Height )
.Overlay( Bitmap | ImageProcess | FilePath, Placer )
.Opacity( 0 ~ 1 )
// 品質控制
.Format( ImageFormat.Jpeg, ... )
.Quality( 0 ~ 100 )
// 記憶與還原點
.Store( Name )
.Fetch( Name )
// 存檔
.Save( Stream | FilePath )
</pre>
<p>這是將第一階段我想做好的 API 全部列表,實際上還陸續增加了些。</p>
<p>身為作者,當然是覺得介面應該還不會太囉唆啦,有建議的話在麻煩提供給我進行改善喔 XD</p>
<p>下面這個例子,是一般情況下可能會用到的呼叫方式,這樣的程式碼就可以完成一次縮圖的處理。</p>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">
ImageProcess
.Handle( Stream | Bitmap | FilePath )
.Resize( Length, Side.Width | Side.Height )
.Save( Stream | FilePath )
</pre>
</li>
<li>控制 .NET 圖形處理後存檔 JPEG 的品質問題
<p>過去不知道,誤認壓縮品質為這是無法控制的 .NET 程式庫結果,事實上,在網路上也大多是看到 <code>InterpolationMode.HighQualityBicubic</code> 設定的相關文章,更加惱怒的是,網路上充斥著抄襲的部落格文章,實在無恥!啊,講遠了,快回來啊,這點改善也可以肉眼就發現,卻是相當有限的。</p>
<p>如果你的縮圖尺寸事實上不大,那麼差不多可以忽略品質問題,因為圖形小,視覺的問題不會那麼明顯,可是如果是大一點的圖,像是 Width 大於 400px,那麼 JPEG 的壓縮存檔可能會破壞掉圖像的品質,肉眼就看得很清楚,我過去就是不知道該怎麼調整 JPEG 存檔壓縮率,所以誤以為無法調整。</p>
<p>所幸耐著性子找到 <a href="http://geeksofweb.blogspot.tw/2010/04/high-quality-image-thumbnails-in-c.html" target="_blank">參考文章</a> 終於知道調整的地方在哪</p>
<p>(在這之前,我實在無法在 MSDN 上找到類似的例子,可能找文章的方向錯誤吧)</p>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [17, 19, 21];">
public static void SaveAsQualityFactor(Bitmap bitmap, Stream stream, ImageFormat format = null, long? quality = null)
{
if (format == null)
{
format = ImageFormat.Jpeg;
}
if (quality.HasValue)
{
var codec = ImageCodecInfo
.GetImageEncoders()
.FirstOrDefault(x => x.FormatDescription.Equals(format.ToString(), StringComparison.CurrentCultureIgnoreCase));
if (codec != null)
{
//Set the parameters for defining the quality of the thumbnail... here it is set to 100%
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality.Value);
bitmap.Save(stream, codec, encoderParameters);
}
else
{
bitmap.Save(stream, format);
}
}
else
{
bitmap.Save(stream, format);
}
}
</pre>
<p>那些關鍵程式行顯示出,藉由對於 codec 的細部參數控制,你可以設定出無損格式的 JPEG 存檔,當然檔案一定會隨之變大,所以我實務上僅在關鍵圖片上以幾乎無損方式存檔(理論上吧 XD),其他小縮圖則不考慮這樣處理</p>
</li>
</ol>
<p>那麼就動手來做吧,咻一下程式就可以寫完了,程式碼有點長,直接由文末的附件下載就好了</p>
<p>本來想說這篇文簡單寫一下就好,沒想到還是多花了些時間,感覺有需要時候的再來探討程式碼,現在就先跳轉到用 LINQPad 來測試 ImageProcess 類別:</p>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">
var stamp = ImageProcess
// 讀檔
.Handle( "浮水印圖檔.png" )
// Debug 資訊
.TakeBitmap( x => x.Dump() )
// 縮小比例 12%
.Resize( 0.12, Side.Width )
// 透明度 80%
.Opacity( 0.8 )
// Debug 資訊
.TakeBitmap( x => x.Dump() );
ImageProcess
// 讀檔
.Handle( "原圖.jpg" )
// Debug 資訊
.Size( x => x.Dump() )
// 圖寬縮為 461px, 圖高依計算比例縮小
.Resize( 461, Side.Width )
// Debug 資訊
.Size( x => x.Dump() )
// 灰階化
.GrayScale()
// 記憶起來
.Store( "A" )
// 四角落浮水印
.Overlay( stamp, (s1, s2) => new Point(10, 10) )
.Overlay( stamp, (s1, s2) => new Point(s1.Width - s2.Width - 10, s1.Height - s2.Height - 10) )
.Overlay( stamp, (s1, s2) => new Point(s1.Width - s2.Width - 10, 10) )
.Overlay( stamp, (s1, s2) => new Point(10, s1.Height - s2.Height - 10) )
// 100% 無損格式
.Quality( 100 )
// JPEG
.Format( ImageFormat.Jpeg )
// 存檔
.Save( "合成輸出.jpg" )
// PNG
.Format( ImageFormat.Png )
// 存檔
.Save( "合成輸出.png" )
// Debug 資訊
.TakeBitmap( x => x.Dump() )
// 提取記憶點
.Fetch( "A" )
// Debug 資訊
.TakeBitmap( x => x.Dump() );
</pre>
<a href="http://goo.gl/jI5O15" target="_blank">[Gist] ImageProcess.cs</a>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-59073683634293406952013-07-12T13:06:00.001+08:002013-07-15T12:53:16.524+08:00跨網站利用 document.write 顯示 ASP.NET MVC ViewResult 內容, 使用 ActionFilter<p>一直忙於工作,好久沒有更新部落格了 (其實列了很多 backlog 都沒有時間整理完 XD),最近在實作專案的過程中,遇上題目所談到這個小小問題,雖然已經先解決了,只是一直不甘心使用了不是很喜歡的作法,久久掛念在心上,今天突然想到有簡單的方式可以解決,很慚愧的來補一篇</p>
<p>文章題目是經過斟酌過才決定的,已經將原本複雜的問題情境都拿掉,最終目的就是需要一個可以跨網站顯示 View 的簡單方式</p>
<a name='more'></a>
<h2>研究</h2>
<p>解決的想法來自於同樣是工作中所接觸到的網路廣告系統,網路廣告內容通常都是由遠端廣告主機拉下來的 html code,因為這件事情如果要依賴網站自行上傳廣告內容,以及安排露出時間表,那個廣告所能觸及的網站就會非常受限,廣告普及的難度就提高了許多</p>
<p>因此所以接觸過的人就知道要植入網路廣告,通常靠的是埋 code 就好,來提升部署速度,我目前接觸到廣告投放有幾種方式</p>
<ul>
<li>iframe</li>
<li>script (document.write)</li>
</ul>
<p>廣告內容、格式以及時間表都由專責伺服器安排,這個 scenario 跟我所要的結果幾乎是一模一樣,那就來借用一下網路廣告的智慧吧 XD</p>
<p>對應以上作法,我需要把我的 View 顯示到他人網站上,應該這麼做:</p>
<ul>
<li>讓對方嵌入 iframe,src 指向我們為他設計的 view 的 URL</li>
<li>讓對方嵌入 script,src 指向我們為他設計的 view 的 URL,並透過 script 中的 document.write 將 View 結果寫到他人網頁上</li>
</ul>
<blockquote>NOTE: 這裡就暫時先不討論兩個作法的優缺點、衍生的問題與限制</blockquote>
<p>第一點應該隨便都可以達到,第二點就需要思考一下作法了</p>
<h2>假設與模擬</h2>
<p>假設我的 View 輸出結果是
<pre type="syntaxhighlighter" class="brush: html; highlight: [];">
<ul>
<li></li>
<li></li>
</ul>
</pre>
</p>
<p>
我希望別人埋的廣告 code (埋這樣的 code 夠簡單了吧...)
<pre type="syntaxhighlighter" class="brush: html; highlight: [];">
<script type="type/javascript" src="http://test-server/Ad/StackBanner"></script>
</pre>
</p>
<p>
廣告 code 最後應該是輸出
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">
document.write("<ul><li></li><li></li></ul>");
</pre>
</p>
<p>這樣一來就可以達到預期目標! (應該不會跳太快吧... :P)</p>
<h2>實作</h2>
<p>好的,現在我們已經將問題範圍限縮到如何將 View 輸出結果轉為 document.write 就好,靈機一動,這或許是一個利用 <a href="http://msdn.microsoft.com/en-us/library/dd410209(v=vs.100).aspx" target="_blank">ActionFilter</a> 的好時機,我想將 View 的顯示結果轉為字串之後,就能夠做到 document.write 了</p>
<p>那麼就來儘速實作這個 ActionFilter 吧</p>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [13,14,15,16,17,18,19,20,21,22,23,24, 26];">
public class ScriptalizedOutputAttribute : ActionFilterAttribute
{
public bool WrapScriptTag { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var response = filterContext.HttpContext.Response;
response.Filter = new StreamFilter(response.Filter, s =>
{
s = Regex.Replace(s, @"(\r\n|\n|\r)", "\\n"); // 換行字元都改掉
s = Regex.Replace(s, @"\""", "\\\""); // 要考慮逸出字元
if (WrapScriptTag)
{
return string.Format("<script type=\"text/javascript\">\n//<!--\ndocument.write(\"{0}\");\n//-->\n</script>", s);
}
return string.Format("document.write(\"{0}\");", s);
});
response.ContentType = "text/javascript";
}
class StreamFilter : Stream
{
private Stream _shrink;
private Func<string, string> _filter;
public StreamFilter(Stream shrink, Func<string, string> filter)
{
_shrink = shrink;
_filter = filter;
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return true; } }
public override bool CanWrite { get { return true; } }
public override void Flush() { _shrink.Flush(); }
public override long Length { get { return 0; } }
public override long Position { get; set; }
public override int Read(byte[] buffer, int offset, int count)
{
return _shrink.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _shrink.Seek(offset, origin);
}
public override void SetLength(long value)
{
_shrink.SetLength(value);
}
public override void Close()
{
_shrink.Close();
}
public override void Write(byte[] buffer, int offset, int count)
{
// capture the data and convert to string
byte[] data = new byte[count];
Buffer.BlockCopy(buffer, offset, data, 0, count);
string s = Encoding.UTF8.GetString(buffer);
// filter the string
s = _filter(s);
// write the data to stream
byte[] outdata = Encoding.UTF8.GetBytes(s);
_shrink.Write(outdata, 0, outdata.GetLength(0));
}
}
}
</pre>
<p>這一個手法基本上來自 <a href="http://arranmaclean.wordpress.com/2010/08/10/minify-html-with-net-mvc-actionfilter/" target="_blank">Minify HTML with .NET MVC ActionFilter</a> 這篇文章,只是原來的作用是在於壓縮 View 內容中的空白字元</p>
<p>我更換了一下使用情境,只是單純的將 View 的結果 script 化,因為這樣一來要在他人網站上利用 document.write 顯示我們網站 View 內容的作法就變為可能了</p>
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">
document.write("<ul><li></li><li></li></ul>");
</pre>
<p>
最後我的 Controller/Action 套上 ScriptalizedOutputAttribute 寫法變成
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [3];">
public class AdController : ControllerBase
{
[ScriptalizedOutput]
public ActionResult StackBanner()
{
// ...
return View(); // 這裡還是單純 render HTML 就好
}
}
</pre>
</p>
<p>就達到原來的目標啦,順利完成,我沒有特意針對特定 MVC 版本設計,按照概念來看 ASP.NET MVC 2 以上應該都適用這招 (我沒寫過更早之前的版本,歡迎提供見解),先整理到此囉。</p>
<blockquote><strong>2013/7/15 提示: </strong>能夠 document.write 到其他網站,當然也可以在自己網站內使用,這一個方式可以有效處理 OutputCache 頁面中不想被 cache 的區塊喔,同樣情境下與 Ajax 方法比較,可以避免掉 Ajax 方法通常需要在 document ready 之後才會進行的缺點。</blockquote>
<p>排版粗獷,用字鄙俗,還請多多海涵 XD</p>
<p>Keywords: ASP.NET MVC, ActionFilter, ActionResult, ViewResult, document.write</p>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com1tag:blogger.com,1999:blog-8962867040946426762.post-82861555364958293062011-12-20T22:35:00.001+08:002012-01-12T14:03:00.883+08:00取得繁體中文字元筆劃數 (Unicode)<p>
繼<a href="http://dinowang.blogspot.com/2011/12/get-traditional-chinese-character.html">上回</a>以 big5 內碼分區查表方式取得中文字元筆劃數後,因無法納入 big5 字集的難字部分無法處理成為先天限制,使用起來頗為不快,不能滿意原解決方案。
</p>
<p>
於是繼續尋找可行方案,找到了 Unihan 統漢字資料庫,發現其資料十分豐富,倉頡碼、同義字、注音、筆劃數、部首筆劃數、... 等等資訊(沒全參透,就不完整列舉了),改天會再整理更多應用心得,這邊就先取用我需要的字元筆劃部分。應急 :)
</p>
<p>
Unihan 資料庫以純文字格式提供為多個檔案,我需要的筆劃資訊都存放在 Unihan 資料庫中的 Unihan_DictionaryLikeData.txt 檔,檔案格式不難解析:
</p>
<a name='more'></a>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [2, 5, 7];">
U+3400 kCangjie TM
U+3400 kTotalStrokes 5
U+3401 kCangjie MOW
U+3401 kCihaiT 37.103
U+3401 kTotalStrokes 6
U+3402 kCangjie PPP
U+3402 kTotalStrokes 6
</pre>
<p>
如列表,每行以 tab 分隔,有三個重要資訊:編碼、屬性名稱、屬性資料。
</p>
<p>
若作為元件而且只是對應筆劃,暫時還不想動用到資料庫,我解析了 Unihan 資料庫中的 Unihan_DictionaryLikeData.txt 檔案,並將筆劃資訊存於 stream 中,並以 Unicode 字碼值,移動指標(查表)來迅速地取得筆劃資訊,主要程式碼如下:
</p>
<p>StrokeLookup.cs</p>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
namespace Unihan
{
public class StrokeLookup : IDisposable
{
private static StrokeLookup _instance;
public static StrokeLookup Instance
{
get
{
if (_instance == null)
{
_instance = new StrokeLookup();
}
return _instance;
}
}
// 利用 stream,存放筆劃資訊,以位移值取得筆劃數
private Stream _stream;
private StrokeLookup()
{
InitialLookupTable();
}
private void InitialLookupTable()
{
var binPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var dataPath = Path.Combine(binPath, "Unihan.Data");
var filePath = Path.Combine(dataPath, "Unihan_DictionaryLikeData.txt");
var lookupPath = Path.Combine(dataPath, "Unihan_DictionaryLikeData.strokes");
// 未曾產生或者 Unihan.Data 目錄中的 Unihan 資料庫有更新,則重新產生查表檔
// 這裡若改以 hash code 去偵測來源檔案是否有變化會更恰當
if (!File.Exists(lookupPath) || File.GetLastWriteTime(filePath) > File.GetLastWriteTime(lookupPath))
{
using (var stream = new FileStream(lookupPath, FileMode.Create, FileAccess.ReadWrite))
{
GenerateStrokeData(filePath, stream);
}
}
// 若改為以 MemoryStream 載入查表資料,也可以善用記憶體優勢
_stream = new FileStream(lookupPath, FileMode.Open, FileAccess.Read);
}
private void GenerateStrokeData(string filePath, Stream outputStream)
{
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = new StreamReader(stream))
{
var line = string.Empty;
while ((line = reader.ReadLine()) != null)
{
// 非有效行
if (string.IsNullOrEmpty(line) || !line.StartsWith("U+"))
{
continue;
}
// 每行只切為三分
var datas = line.Split(new[] { '\t', ' ' }, 3, StringSplitOptions.RemoveEmptyEntries);
// 格式不符或不含有筆劃資訊就忽略
if (datas.Length < 3 || datas[1] != "kTotalStrokes")
{
continue;
}
// U+3400 轉為 uint
var hex = datas[0].Substring(2);
var code = uint.Parse(hex, NumberStyles.HexNumber);
// 取得筆劃資訊
var stroke = byte.Parse(datas[2]);
// Padding 補足間隙的不存在字元
var gap = code - outputStream.Length;
if (gap > 1)
{
outputStream.Seek(0, SeekOrigin.End);
while (gap-- > 1)
{
outputStream.WriteByte(0);
}
}
outputStream.Seek(code, SeekOrigin.Begin);
outputStream.WriteByte(stroke);
}
}
}
}
public IEnumerable<CharStroke> GetStrokes(string source)
{
foreach (var chr in source)
{
yield return new CharStroke { Character = chr, Stroke = GetStroke(chr) };
}
}
public int GetStroke(char source)
{
var code = (uint)source;
if (code >= 0 && code < _stream.Length)
{
_stream.Seek(code, SeekOrigin.Begin);
return _stream.ReadByte();
}
return 0;
}
public void Dispose()
{
if (_stream != null)
{
_stream.Dispose();
}
}
}
}
</pre>
<p>CharStroke.cs</p>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">
namespace Unihan
{
public class CharStroke
{
public char Character { get; set; }
public int Stroke { get; set; }
}
}
</pre>
<p>
過程中曾經思考直接在類別中以靜態成員 Dictionary<char, byte> 儲存所有字元以及對應筆劃,這樣做在速度上並不慢,程式也好寫,可是頗為消耗記憶體;後來改用了 byte[] 以基底位置加上位移值去計算,想要快速地得到筆劃數,不過耗用記憶體的情形依舊,只是比前者要好一些,後來這兩種方式都放棄了。
</p>
<p>
範例方案完整原始碼 <a href="https://sites.google.com/site/dinowang64/blog/Unihan.7z">請由此下載</a>,若有任何問題歡迎提供意見,謝謝! :)
</p>
<h4>參考資料</h4>
<ul>
<li><a href="http://www.unicode.org/charts/unihan.html">Unihan Database</a></li>
<li><a href="ftp://ftp.unicode.org/Public/UNIDATA/Unihan.zip">下載 Unihan 資料庫 (.zip 格式)</a></li>
<li><a href="http://irw.ncut.edu.tw/peterju/unicode.html">Unicode 筆記</a></li>
</ul>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-43359873368822197762011-12-16T16:09:00.001+08:002012-01-11T22:13:56.053+08:00取得繁體中文字元筆劃數<backquote style="color: red; font-weight: bold;">
完整的中文筆畫查表方法,已另於 <a href="http://dinowang.blogspot.com/2011/12/how-to-get-chinese-character-strokes.html">取得繁體中文字元筆劃數 (Unicode)</a> 中提供,此為過渡時期方法,無法處理罕見字集。
</backquote>
<p>
專案中的某項功能需要依中文字筆劃數分組顯示,例如:1~5劃、6~10劃...等,因此產生了查得中文字元筆劃的需求。
</p>
<p>
本來呢寄望 Windows 或 IME 能提供相關的 API,但似乎沒那麼容易,網路上多數是轉 BIG5 後以內碼分區的查表方式取得筆劃數,查到有 <a href="http://www.blueshop.com.tw/board/show.asp?subcde=BRD2007092010385369D">PHP</a>、<a href="http://www.dotblogs.com.tw/jeff-yeh/archive/2010/11/08/19291.aspx">C#</a>、<a href="http://raymond-weng.blogspot.com/2010/08/as3-big5.html">ActionScript</a>、<a href="http://www.javaworld.com.tw/jute/post/view?bid=35&id=109602&sty=1">Java</a> 這些例子,一篇篇讀下來解決方案如出一轍,應該是個穩定的方式吧!?
</p>
<p>
有了! 其中的 C# 範例是我想要的,但我更想利用 extension method 實作,所以就動手修改了一下代碼如下:
</p>
<a name='more'></a>
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">
public static class CharExtension
{
private static Encoding _big5 = Encoding.GetEncoding(950);
/// <summary>
/// 取得繁中字元筆劃數
/// </summary>
/// <param name="chr"></param>
/// <returns></returns>
public static int TraditionalChineseStroke(this char chr)
{
var bytes = _big5.GetBytes(new[] { chr });
if (bytes.Length == 1)
{
// 傳入一般的 ASCII 字元,如:A-F, 0-9 之類的
return 0;
}
var code = (bytes[0] * 256) + bytes[1];
return TraditionalChineseStrokeLookup(code);
}
/// <summary>
/// 來自 GitHub 上有其他專案準備好的 big5_stroke.tab https://gist.github.com/1150937/
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
private static int TraditionalChineseStrokeLookup(int code)
{
if (code >= 0xa440 && code <= 0xa441) { return 1; }
if (code >= 0xa442 && code <= 0xa453) { return 2; }
if (code >= 0xa454 && code <= 0xa47e) { return 3; }
if (code >= 0xa4a1 && code <= 0xa4fd) { return 4; }
if (code >= 0xa4fe && code <= 0xa5df) { return 5; }
if (code >= 0xa5e0 && code <= 0xa6e9) { return 6; }
if (code >= 0xa6ea && code <= 0xa8c2) { return 7; }
if (code >= 0xa8c3 && code <= 0xab44) { return 8; }
if (code >= 0xab45 && code <= 0xadbb) { return 9; }
if (code >= 0xadbc && code <= 0xb0ad) { return 10; }
if (code >= 0xb0ae && code <= 0xb3c2) { return 11; }
if (code >= 0xb3c3 && code <= 0xb6c3) { return 12; }
if (code >= 0xb6c4 && code <= 0xb9ab) { return 13; }
if (code >= 0xb9ac && code <= 0xbbf4) { return 14; }
if (code >= 0xbbf5 && code <= 0xbea6) { return 15; }
if (code >= 0xbea7 && code <= 0xc074) { return 16; }
if (code >= 0xc075 && code <= 0xc24e) { return 17; }
if (code >= 0xc24f && code <= 0xc35e) { return 18; }
if (code >= 0xc35f && code <= 0xc454) { return 19; }
if (code >= 0xc455 && code <= 0xc4d6) { return 20; }
if (code >= 0xc3d7 && code <= 0xc56a) { return 21; }
if (code >= 0xc56b && code <= 0xc5c7) { return 22; }
if (code >= 0xc5c8 && code <= 0xc5c7) { return 23; }
if (code >= 0xc5f1 && code <= 0xc654) { return 24; }
if (code >= 0xc655 && code <= 0xc664) { return 25; }
if (code >= 0xc665 && code <= 0xc66b) { return 26; }
if (code >= 0xc66c && code <= 0xc675) { return 27; }
if (code >= 0xc676 && code <= 0xc67a) { return 28; }
if (code >= 0xc67b && code <= 0xc67e) { return 29; }
if (code >= 0xc940 && code <= 0xc944) { return 2; }
if (code >= 0xc945 && code <= 0xc94c) { return 3; }
if (code >= 0xc94d && code <= 0xc95c) { return 4; }
if (code >= 0xc95d && code <= 0xc9aa) { return 5; }
if (code >= 0xc9ab && code <= 0xc959) { return 6; }
if (code >= 0xca5a && code <= 0xcbb0) { return 7; }
if (code >= 0xcbb1 && code <= 0xcddc) { return 8; }
if (code >= 0xcddd && code <= 0xd0c7) { return 9; }
if (code >= 0xd0c8 && code <= 0xd44a) { return 10; }
if (code >= 0xd44b && code <= 0xd850) { return 11; }
if (code >= 0xd851 && code <= 0xdcb0) { return 12; }
if (code >= 0xdcb1 && code <= 0xe0ef) { return 13; }
if (code >= 0xe0f0 && code <= 0xe4e5) { return 14; }
if (code >= 0xe4e6 && code <= 0xe8f3) { return 15; }
if (code >= 0xe8f4 && code <= 0xecb8) { return 16; }
if (code >= 0xecb9 && code <= 0xefb6) { return 17; }
if (code >= 0xefb7 && code <= 0xf1ea) { return 18; }
if (code >= 0xf1eb && code <= 0xf3fc) { return 19; }
if (code >= 0xf3fd && code <= 0xf5bf) { return 20; }
if (code >= 0xf5c0 && code <= 0xf6d5) { return 21; }
if (code >= 0xf6d6 && code <= 0xf7cf) { return 22; }
if (code >= 0xf6d6 && code <= 0xf7cf) { return 23; }
if (code >= 0xf8a5 && code <= 0xf8ed) { return 24; }
if (code >= 0xf8e9 && code <= 0xf96a) { return 25; }
if (code >= 0xf96b && code <= 0xf9a1) { return 26; }
if (code >= 0xf9a2 && code <= 0xf9b9) { return 27; }
if (code >= 0xf9ba && code <= 0xf9c5) { return 28; }
if (code >= 0xf9c6 && code <= 0xf9dc) { return 29; }
if (code >= 0xf9da && code <= 0xf9da) { return 9; }
if (code >= 0xf9db && code <= 0xf9db) { return 12; }
if (code >= 0xf9d6 && code <= 0xf9d8) { return 13; }
if (code >= 0xf9dc && code <= 0xf9dc) { return 15; }
if (code >= 0xf9d9 && code <= 0xf9d9) { return 16; }
if (code >= 0xc67b && code <= 0xc67d) { return 30; }
if (code >= 0xf9cc && code <= 0xf9cf) { return 30; }
if (code >= 0xf9c6 && code <= 0xf9c6) { return 31; }
if (code >= 0xf9d0 && code <= 0xf9d0) { return 31; }
if (code >= 0xf9d1 && code <= 0xf9d1) { return 32; }
if (code >= 0xc67e && code <= 0xc67e) { return 33; }
if (code >= 0xf9d2 && code <= 0xf9d2) { return 33; }
if (code >= 0xf9d3 && code <= 0xf9d3) { return 34; }
if (code >= 0xf9d4 && code <= 0xf9d5) { return 36; }
return 0;
}
}
</pre>
<p style="color: red;">
經過一番測試,這方法不盡理想,例如:「堃」字在 BIG5 中並不存在,因此在轉換到 BIG5 bytes 的過程中無法順利轉換為雙位元(而是轉成 "?" 的 ASCII code 63),為此我應該會尋找更合適的方法,整理後再分享。
</p>
<p>
查表資料參考 GitHub 上其他專案準備好的 <a href="https://gist.github.com/1150937/">big_stroke.tab</a>,由於不認為這份資料會有異動性,所以就不想由外部載入了,用 regular expression 稍微動手一下,整理起 code 挺方便 :)
</p>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-15517126174555792522011-07-14T03:17:00.009+08:002011-07-14T21:56:26.208+08:00如何得知一個 Console Application 被終止今天被問了一個問題:如何得知一個 Console Application 被終止。<br />
<br />
著實嚇了一跳! 畢竟很常開 Console Application 類型的專案是沒錯啦,不過通常只用來寫些 sample code。<br />
<br />
不太會在正式的應用上再寫成 Console Application 了,這種問題感覺就是"那A加你甘丹",還真是連想都沒想過。<br />
<br />
因為在放暑假的關係 XD,既然有空檔,那就來研究看看好了,要解決這個問題,初步的構想是從 AppDomain 著手。<br />
<a name='more'></a><br />
透過 <strike>Intellisense</strike> MSDN 文件發現 AppDomain 是有 event 的,看來可用的 event 有:<br />
<ul><li>DomainUnload</li>
<li>ProcessExit</li>
<li>UnhandledException</li>
</ul><br />
好吧,既然有方向那就動工先寫個雛型囉,先來段程式碼:<br />
<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [];">class Program
{
static void Main(string[] args)
{
var domain = AppDomain.CurrentDomain;
domain.DomainUnload += (s, e) =>
{
Console.WriteLine("AppDomain Unload");
};
domain.ProcessExit += (s, e) =>
{
Console.WriteLine("Process Exit");
};
domain.UnhandledException += (s, e) =>
{
Console.WriteLine("Unhandled Exception");
};
Console.WriteLine("按下 [E] 引發 exception 結束程式, 其他任意鍵結束程式");
if (Console.ReadKey().KeyChar == 'E')
{
throw new Exception();
}
}
}
</pre><br />
這 code 是為了先釐清 AppDomain 事件測試而寫的,執行這個程式的話會有幾個發現:<br />
<ol><li>因未捕捉到的 Exception 而結束的話,會引發 AppDomain 中的 UnhandledException</li>
<li>正常的程式結束,會引發 AppDomain 中的 ProcessExit</li>
<li style="color: red;">經由 Ctrl+C, Ctrl+Break 以及視窗關閉等等操作結束的話,AppDomain 的任何一個事件都抓不到</li>
</ol><br />
可是朋友的問題,就是希望能抓到視窗被使用者關閉,而非應用程式自行結束(包含發生 Exception)。<br />
<br />
這下囧了,如同第 3 點所發現的,沒有一個事件會捕捉到視窗被關閉<strike>,想說那我管他去死好哩</strike>。<br />
<br />
喔,不! 事情當然有轉機的,回到家想一想 google 了一下,發現應由 WinAPI 著手了,最後找到了 SetConsoleCtrlHandler 這個 API,搭配一個自訂的 handler 的話可以達成這位朋友要的效果。<br />
<br />
那麼整合使用 SetConsoleCtrlHandler 後的 code 如下:<br />
<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 26, 27, 29, 31, 32, 33, 34, 35, 36, 37, 38];">class Program
{
static void Main(string[] args)
{
SetConsoleCtrlHandler(t =>
{
// 這邊簡單的顯示導致視窗關閉的來源是什麼
Console.WriteLine(t.ToString());
// 在這裡處理視窗關閉前想要執行的程式碼
// 返回 false 將事件交回原處理函式執行正常關閉
return false;
},
true);
Console.WriteLine("按下任意鍵結束程式");
Console.ReadKey();
Console.WriteLine("程式正常結束");
}
#region WinAPI 相關
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
public delegate bool ConsoleCtrlDelegate(CtrlTypes ctrlType);
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
#endregion
}
</pre><br />
呼叫 SetConsoleCtrlHandler 時必須傳入一委派,以處裡視窗控制事件,結束時必須返回一布林值,SetConsoleCtrlHandler 回傳值與其餘參數可參考 MSDN 文件,不詳加說明。<br />
<br />
SetConsoleCtrlHandler 已經可以準確地抓到 Console Application 的中斷事件,如:Ctrl+C、Ctrl+Break、按下關閉視窗按鈕、以工作管理員結束視窗工作、登出、關機等,實際上測試工作良好,但是須注意無法處理 pskill 這樣的工具所造成的程序中斷喔。<br />
<br />
<p>參考資料:<br />
<ul><li><a href="http://msdn.microsoft.com/en-us/library/ms686016(v=vs.85).aspx" target="_blank">MSDN - SetConsoleCtrlHandler Function</a></li>
<li><a href="http://www.pinvoke.net/default.aspx/kernel32.setconsolectrlhandler" target="_blank">PInvoke.NET - SetConsoleCtrlHandler (Kernal32)</a></li>
</ul></p>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-29425298038533500012010-10-12T01:27:00.006+08:002010-11-27T22:54:43.471+08:00利用 Regex.Replace 的 MatchEvaluator 委派進行樣板字串置換Regular Expression 真的很讚!尤其是在 .NET 中使用 Regex 物件時,你會感覺到非常幸福,不光是支援的中介符號和語法完整 (比較過 Javascript 的 Regular Expression 就知道),連 Regex.Replace 都有特別犀利的地方。<br />
<br />
我們在程式寫作的時候的時候,偶爾會遇到需要處理樣板的情況,例如:<br />
<br />
<blockquote>Hi! <b style="color: red;">XXX</b>您好,您<b style="color: red;">X</b>月的<strike>贍養</strike>管理費未繳,請盡速繳納...</blockquote><a name='more'></a><br />
作為程式設計師,你一定有很多種方法完成這項工作,可能直接用 string 去加出來,也可能用 StringBuilder 做。當然囉!這邊要展示的就是利用 Regex.Replace,參考的程式碼如下:<br />
<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [10, 15, 25];">class Program
{
static void Main(string[] args)
{
string template, result;
// 匿名物件
template = "Hi! [Name]您好,您[Month]月的管理費未繳,請盡速繳納...";
var userObject = new { Name = "Nobody", Month = "五" };
result = template.TemplateSubstitute(userObject);
Console.WriteLine(result); //Hi! Nobody您好,您五月的管理費未繳,請盡速繳納...
// 使用任意物件(讀取屬性並置換)
template = "今天是西元[Year]年[Month]月[Day]日";
result = template.TemplateSubstitute(DateTime.Now);
Console.WriteLine(result); //今天是西元2010年10月12日
}
}
public static class StringExtension
{
public static string TemplateSubstitute(this string input, object data)
{
var type = data.GetType();
return Regex.Replace(input, @"\[(\w+)\]", m =>
{
var name = m.Groups[1].Value;
var prop = type.GetProperty(name);
if (prop != null)
{
// 找到屬性,傳回屬性值執行字串替換
return prop.GetValue(data, null).ToString();
}
else
{
// 在物件中找不到符合名稱的屬性,回傳原值不處理
return m.Value;
}
});
}
}
</pre><br />
範例中使用 Extension method 擴充 String 提供一個 TemplateSubstitute 方法,在這個方法中利用 Regex.Replace 尋找符合樣板規格的 [ ] 區塊,並提供一個 lambda 給 Regex.Replace 作為回呼(Callback),重要的是使用這個方法不需要自行處理字串的組合,或是多次呼叫 Regex.Replace 取得最後的結果,而只要在 lambda 中回傳我要替代的內容就可以囉,很棒吧!<br />
<br />
<p>參考資料</p><ul><li>MSDN - <a href="http://msdn.microsoft.com/zh-tw/library/system.text.regularexpressions.regex.replace(VS.80).aspx" target="_blank">Regex.Replace 方法</a></li>
<li><a href="http://dotnetperls.com/regex-replace" target="_blank">C# Regex.Replace and MatchEvaluator</a></li>
</ul>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-91356080724127960952010-10-10T16:57:00.009+08:002012-01-17T22:56:01.527+08:00使用 NPOI 在匯出的 Excel 檔中逐列內嵌圖片相信很多人都有使用 NPOI 匯出 Excel 工作表的經驗了,這次來分享一下在 Excel 中內嵌圖片的範例。<br />
<br />
在分享的案例中,我將使用 ASP.NET MVC / C# 利用 Google 搜尋圖片的功能,將關鍵字搜尋到的圖片,導出至 Excel 工作表中的每一列,見圖如下:<br />
<br />
<p><a href="http://lh6.ggpht.com/_FnHqhybRFAs/TLFGbLkUsoI/AAAAAAAABAo/Y2E9uxkeNsU/s1600-h/image%5B3%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_FnHqhybRFAs/TLFGb82r84I/AAAAAAAABAs/K7CpgtIHc18/image_thumb%5B1%5D.png?imgmax=800" width="508" height="307"></a></p><br />
匯出的 Excel 內容為搜尋到的每張圖片,不過我沒將圖片完整輸出,而是產生縮圖後才輸出。<a name='more'></a><br />
<br />
<p><a href="http://lh4.ggpht.com/_FnHqhybRFAs/TLF6laqRmyI/AAAAAAAABBA/LK8oi2446RY/s1600-h/image%5B11%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FnHqhybRFAs/TLF6mASvwoI/AAAAAAAABBE/pT8v4u7u2CE/image_thumb%5B6%5D.png?imgmax=800" width="459" height="519"></a></p><br />
因為重點在以 NPOI 產生內嵌圖片的 Excel 工作表,因此我假設讀者都有使用 NPOI 的基礎,只對產生 HSSFWorkbook 的關鍵程式碼說明,其餘的部分可以參考我上傳的 ASP.NET MVC 2 專案,就不另行解釋。<br />
<br />
實際上利用 NPOI 產生 workbook 的程式碼如下,關於本文主旨如何利用 NPOI 在 Excel 中插入圖片,特別留意的就是高亮的那幾行程式碼。<br />
<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [11, 84, 87, 88, 89, 90, 91, 95, 96];">private HSSFWorkbook GenerateSearchResult(IEnumerable<string> urls)
{
int rowIndex = 0;
int cellIndex = 0;
int pictureIndex = 0;
var request = new WebClient();
var workbook = new HSSFWorkbook();
var sheet = workbook.CreateSheet("Search Result");
var patriarch = sheet.CreateDrawingPatriarch();
#region 樣式
HSSFCellStyle headerStyle = workbook.CreateCellStyle();
headerStyle.Alignment = HSSFCellStyle.ALIGN_CENTER;
headerStyle.VerticalAlignment = HSSFCellStyle.VERTICAL_CENTER;
headerStyle.FillBackgroundColor = HSSFColor.BLACK.index;
headerStyle.FillPattern = HSSFCellStyle.SOLID_FOREGROUND;
headerStyle.BorderTop = headerStyle.BorderLeft =
headerStyle.BorderRight = headerStyle.BorderBottom = 1;
HSSFFont headerFont = workbook.CreateFont();
headerFont.Color = HSSFColor.WHITE.index;
headerFont.Boldweight = HSSFFont.BOLDWEIGHT_BOLD;
headerStyle.SetFont(headerFont);
HSSFCellStyle cellStyle = workbook.CreateCellStyle();
cellStyle.Alignment = HSSFCellStyle.ALIGN_LEFT;
cellStyle.VerticalAlignment = HSSFCellStyle.VERTICAL_CENTER;
cellStyle.BorderTop = cellStyle.BorderLeft =
cellStyle.BorderRight = cellStyle.BorderBottom = 1;
cellStyle.WrapText = true;
#endregion
#region 表頭參數
var headers = new[]
{
new { Caption = "URL", Width = 40 },
new { Caption = "Picture", Width = 20 }
};
#endregion
#region 表頭
HSSFRow row = sheet.CreateRow(rowIndex++);
HSSFCell cell;
foreach (var header in headers)
{
if (header.Width > 0)
{
sheet.SetColumnWidth(cellIndex, header.Width * 256);
}
cell = row.CreateCell(cellIndex++);
cell.SetCellValue(header.Caption);
cell.CellStyle = headerStyle;
}
HSSFCell colorHeader = row.GetCell(cellIndex - 1);
#endregion
#region 表身
foreach (var url in urls)
{
row = sheet.CreateRow(rowIndex++);
cellIndex = 0;
cell = row.CreateCell(cellIndex++);
cell.CellStyle = cellStyle;
cell.SetCellValue(url);
cell = row.CreateCell(cellIndex++);
cell.CellStyle = cellStyle;
try
{
// 下載圖片
var image = Image.FromStream(request.OpenRead(new Uri(url)));
// 產生縮圖
decimal sizeRatio = ((decimal)image.Height / image.Width);
int thumbWidth = 100;
int thumbHeight = decimal.ToInt32(sizeRatio * thumbWidth);
var thumbStream = image.GetThumbnailImage(thumbWidth, thumbHeight, () => false, IntPtr.Zero);
var memoryStream = new MemoryStream();
thumbStream.Save(memoryStream, ImageFormat.Jpeg);
// 將縮圖加入到 workbook 中
pictureIndex = workbook.AddPicture(memoryStream.ToArray(), HSSFWorkbook.PICTURE_TYPE_JPEG);
// 將縮圖定位到 worksheet 中
var anchor = new HSSFClientAnchor(0, 0, 0, 0, cell.CellNum, row.RowNum, 0, 0);
var picture = patriarch.CreatePicture(anchor, pictureIndex);
var size = picture.GetImageDimension();
row.HeightInPoints = size.Height;
picture.Resize();
// 為了不讓圖片壓線,必須讓圖片有一點位移,你可以把它移除掉看看會產生什麼情況
// (我得承認這裡是程式中的魔術數字 Orz,但是一時找不到更好的方法)
anchor.Dx1 = 5;
anchor.Dy1 = 2;
}
catch (Exception ex)
{
// 圖片載入失敗,顯示錯誤訊息
cell.SetCellValue(ex.Message);
}
}
#endregion
return workbook;
}
</pre><p>參考資料</p><ul><li>本文程式碼下載:<a href="https://sites.google.com/site/dinowang64/blog/ExcelEmbedPictures.rar">ExcelEmbedPictures.rar</a><br />
<li><a href="http://npoi.codeplex.com/">NPOI</a> <br />
<li><a href="http://office.microsoft.com/zh-tw/excel-help/HP010073849.aspx">Excel 的規格及限制</a><br />
</ul>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com11tag:blogger.com,1999:blog-8962867040946426762.post-37585074216280016892010-10-10T01:58:00.015+08:002010-10-10T20:37:51.578+08:00懶人的 VBA 程式寫作法<p>由於某個衝動的承諾,最近再次接觸到 VBA (Visual Basic for Application) 程式碼,對 Excel 工作表進行客製化。雖然需求挺簡單的,程式碼也不多,可是寫 VBA 這件事對我來說,並不是像寫 C#/.NET 一樣容易遇上,再加上 VBA 中很重要的 Office Object Model 還挺複雜的,小弟實在沒辦法塞太多 Office Object Model 的東東在腦子裡,這時候要靠文件嗎?可能一個小小的需求就會查到我天昏地暗,寫不下去…<br />
<br />
所以在這邊分享一個小方法,能讓你快速的使用 VBA 完成你的工作,懂得一些 VB 的邏輯與控制結構語法即可,關於 Office Object Model 就借助工具幫忙。<a name='more'></a><br />
<br />
以 Excel 來看好了,這是一套非常強大的應用軟體,可以支持你進行非常複雜的運算,可是在資料顯示的需求上,我們常常會有自己的想法或需求,Excel 不見得能完全滿足你,像是下面這張圖,如果不寫些程式,你可能每次都得自己動手設定。<br />
<p><a href="http://lh6.ggpht.com/_FnHqhybRFAs/TLCqDHmxIWI/AAAAAAAAA_4/TH1OhytYyPE/s1600-h/2010-10-10_010005%5B4%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="2010-10-10_010005" border="0" alt="2010-10-10_010005" src="http://lh5.ggpht.com/_FnHqhybRFAs/TLCqDxPxobI/AAAAAAAAA_8/9gfgUQ66qec/2010-10-10_010005_thumb%5B2%5D.png?imgmax=800" width="382" height="424"></a></p>你可能馬上會想到 Excel 有條件化條件的功能可以用,但是它卻只能對單一儲存格設定格式,由於找不到可以達成我目的的設定方式,所以只好寫程式解決啦。<br />
<p><a href="http://lh4.ggpht.com/_FnHqhybRFAs/TLCqEU_O8kI/AAAAAAAABAA/2Kr8rLartBU/s1600-h/image%5B3%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_FnHqhybRFAs/TLCqFIhuESI/AAAAAAAABAE/mkjmT4jQ3_4/image_thumb%5B1%5D.png?imgmax=800" width="356" height="386"></a></p>在你可能得先設定一下 Excel 以開啟 VBA 的設計工具,請參考<a href="http://www.dotblogs.com.tw/chou/archive/2010/04/19/14690.aspx">小歐ou大的這篇文章</a>,先完成設定,接下來錄製設定格式的巨集,操作如下:<br />
<p><a href="http://lh5.ggpht.com/_FnHqhybRFAs/TLCqFy4Z1DI/AAAAAAAABAI/Y1DI2_6O-fY/s1600-h/image%5B11%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_FnHqhybRFAs/TLCqGeF8k9I/AAAAAAAABAM/Qhvnf0KKcT4/image_thumb%5B7%5D.png?imgmax=800" width="713" height="136"></a></p>開始錄製巨集第一個巨集,選取任一列並進行底色的設定,如圖:<br />
<p><a href="http://lh5.ggpht.com/_FnHqhybRFAs/TLCqG3tBKOI/AAAAAAAABAQ/6tds24NaxT0/s1600-h/image%5B15%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_FnHqhybRFAs/TLCqHrqrXOI/AAAAAAAABAU/mVm4v0pV3dc/image_thumb%5B9%5D.png?imgmax=800" width="306" height="279"></a></p>然後就停止錄製,按下 Visual Basic 按鈕<br />
<p><a href="http://lh4.ggpht.com/_FnHqhybRFAs/TLCqIT-iFSI/AAAAAAAABAY/N7iFB-H-uCA/s1600-h/image%5B29%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_FnHqhybRFAs/TLCqJO-NK9I/AAAAAAAABAc/cGhuduHjxuY/image_thumb%5B21%5D.png?imgmax=800" width="714" height="173"></a></p>你就會看到設定這樣的格式時,你需要操作那些 Excel Object Model<br />
<p><a href="http://lh5.ggpht.com/_FnHqhybRFAs/TLCqJy19kzI/AAAAAAAABAg/APBuClqFz3c/s1600-h/image%5B33%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_FnHqhybRFAs/TLCqKcYoo6I/AAAAAAAABAk/bvPODTf8szE/image_thumb%5B23%5D.png?imgmax=800" width="506" height="280"></a></p>然後咧,就是把他 COPY 走,拿到你要用的程式碼那邊去囉,完成這個數量與位置標示的 VBA 程式碼大致如下,高亮的那幾行就是由巨集錄製得來的程式碼,其餘則是我自己完成的程式的邏輯控制結構。<br />
<pre class="brush: vb; highlight: [34, 36, 42, 43, 44, 45, 46, 47, 49, 50, 51, 52, 53, 54]" type="syntaxhighlighter">Private Sub Worksheet_Change(ByVal Target As Range)
' 整批貼上時 Range 的左上角是 A4
If Target.row = 4 And Target.Column = 1 Then
FormatAll
End If
' 如果直接改數量...
If Target.row >= 4 And Target.Column = 5 Then
FormatRow Range("$A" & Target.row & ":$J" & Target.row)
End If
End Sub
Function FormatAll()
Dim row As Integer
row = 4
Dim continue As Boolean
continue = True
While (continue)
If Cells(row, 1) = "" Then
' 一直處理到沒有批號的那列
continue = False
Else
FormatRow Range("$A" & row & ":$J" & row)
row = row + 1
End If
Wend
End Function
Function FormatRow(Target As Range)
' 數量大於1 整列給底色
If Cells(Target.row, 5) > 1 Then
Target.Interior.Color = RGB(255, 153, 204)
Else
Target.Interior.Pattern = xlNone
End If
' 儲位區域不同上方畫實線 (畫雙實線怕列印會換頁)
If Target.row > 4 Then
If Left(Cells(Target.row, 3), 3) <> Left(Cells(Target.row - 1, 3), 3) Then
With Target.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
Else
With Target.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlHairline
End With
End If
End If
End Function
</pre><br />
介紹這個簡單的 Office Object Model 學習方法,這應該對剛接觸 VBA 的人有幫助(我自己比較少碰到有人寫 VBA... XD),但是以錄製方式產生的 VBA 程式碼其實會有很多冗餘的程式碼跑出來,所以個人是建議一開始可以用此方法來學習、快速上手,但是千萬不要不求甚解,多到 MSDN 上查詢相關的資料。<br />
<br />
<p>參考資料</p><ul><li><a href="http://msdn.microsoft.com/en-us/library/y1xatbkd(v=VS.80).aspx">Automating Applications Using the Office Object Model</a> <br />
<li><a href="http://www.dotblogs.com.tw/chou/archive/2010/04/19/14690.aspx">[Office2010]在 Excel 寫 VBA</a></li><br />
</ul>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-11889188028465452212010-10-01T00:12:00.016+08:002010-10-10T17:06:45.049+08:00離開 LINQ 的 Expression Tree (一)話說 .NET Framework 在 3.5 發布以後出現的 LINQ,真令人驚豔!這傢伙威力的強大相信大家早有體會了,而 expression tree (運算式樹) 是在 LINQ 中是非常重要的元素,其重要性看 namespace 就能知道 (System.Linq.Expressions)。</p><p>Expression tree 自此之後開始快速<strike>欺凌</strike>擴展 .NET 程式設計師的視野,應用之廣泛像是 ASP.NET MVC 2 中開始提供在強型別 View 中應用 lambda 運算式 (lambda 也是一種 expression tree) 來作為舊語法無法支援 IntelliSense、編譯時期檢查和 refactoring support 的取代品(強烈建議使用),可參考 <a href="http://weblogs.asp.net/scottgu/archive/2010/01/10/asp-net-mvc-2-strongly-typed-html-helpers.aspx">ScottGu’s Blog - ASP.NET MVC 2: Strongly Typed Html Helpers</a> 這篇文章,相信有經驗的你很快的可以分辨出孰優孰劣。</p><p>我自己則是應用 expression tree 在推論引擎的實作上,先提一下推論引擎。前一段時間為了實作規則庫推論引擎(專家系統的一種分支),花了一段時間研究,這種系統的特性是:一個<strong>規則庫</strong>(Rule Base 或 Knowledge Base)、<strong>工作記憶體</strong>(Working Memory)與<strong>推論引擎</strong>(Inference Engine),架構大致如下:</p><a name='more'></a><br />
<p align="left"><img src="http://obiwannabe.co.uk/html/papers/proc-audio/img5.png"></p><p align="left">你可以開始試想,在規則庫中有一條規則如下,你如何實作 (必須考慮它具有異動性,例如體重大於300kg的豬滿街跑時,>=300kg 的豬就不能叫做肥豬了吧.... 沒養過豬300是隨便舉例的 XD) ? 當規則庫內含有上千條類似這樣的規則時,執行效能又會怎樣? </p><blockquote><p><font color="#0000ff">條件:Sex="M" And Type=”Pig” And Weight>=300<br />
推論:是一頭公肥豬!</font></p></blockquote><p><font color="#ff8040">【註】其實 Outlook 中的管理規則及通知,也很像是一的小型的專家系統,雖然他可能不需要千上百條規則,你有試想過把這樣的特性導入到你的應用程式中嗎? ^^ </font></p><p>為了這個推論引擎,我搜尋了許多參考而歸納了幾個不同的做法,小小作個整理。包含試作和過去做過的案子(使用不同的語言),以下方法大概都算得上有實務經驗:</p><ol><li>解譯型 (interpretation)<br />
每執行一次就解譯一條規則,解譯的過程可能會出現大量的 if、switch...,沒有特殊技術,就是完全邊讀邊跑,像是直譯語言一樣,所以速度上較慢 (自己寫的話搞不好還會比常見的直譯語言更慢)。 相關可參考的開源碼實作有 <a href="http://sourceforge.net/projects/sdsre/">RuleEngine</a>。<br />
<li>編譯型 (compilation) <ul><li>程式碼編譯 (code compilation)<br />
運用動態組成程式碼,如 C# code,利用 System.Runtime.CompilerServices 命名空間中的類別支援,動態編譯出類別。相關可參考的開源碼實作有 <a href="http://nruler.codeplex.com/">NRuler</a>。<br />
<li>運算式樹 (expression tree)<br />
我們可以單獨的把 expression tree 拿出來用,它不一定得跟 LINQ 扯在一起!老實說,我注意到運算式樹的時候,大概就決定了這種作法,它在速度(編譯!)與可用性上兼而有之,真的是很難得,雖然過程中有卡關,那都是自己的不夠理解,我之後會陸續分享卡關經驗。相關開源碼實作我沒找到 (其實是因為自己太想寫了,沒認真找 XD),所以我打算陸續這些實作經驗。</li><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
</ul></li>
</ol><p>下一次,我想就以上的例子提供簡單的實作,希望下一篇文很快就能跟大家見面 XD</p><p> </p><p>參考資料</p><ul><li><a href="http://weblogs.asp.net/scottgu/archive/2010/01/10/asp-net-mvc-2-strongly-typed-html-helpers.aspx">ScottGu’s Blog - ASP.NET MVC 2: Strongly Typed Html Helpers</a><br />
<li><a href="http://en.wikipedia.org/wiki/Expert_system">Wikipedia – Expert System</a><br />
<li>Open source projects <ul><li><a href="http://sourceforge.net/projects/sdsre/">RuleEngine</a><br />
<li><a href="http://nruler.codeplex.com/">NRuler</a><br />
<li><a href="http://sourceforge.net/projects/nxbre/">NxBRE</a><br />
<li><a href="http://droolsdotnet.codehaus.org/">Drools.NET</a></li><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
</ul></li>
</ul><p>NOTE: <a href="http://sourceforge.net/projects/nxbre/">NxBRE</a>、<a href="http://droolsdotnet.codehaus.org/">Drools.NET</a> 是從 Java port 的 .NET 版本。</p>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com1tag:blogger.com,1999:blog-8962867040946426762.post-12082014503336056892010-09-15T12:34:00.004+08:002010-09-18T00:28:05.799+08:00Javascript 的 prototype 特性與使用方法一般我們在 javascript 中使用這種方式進行類別定義與活化類別<br />
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">// 以 function 為建構子
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
//然後活化類別
var p = new Person("Dino", "Wang");
</pre><br />
如你所見這樣的類別並不具有"成員函式",所以當你需要成員函式時你可能會手動繫結上去<br />
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">// 附加一個匿名函式到 p 實體
p.fullName = function () {
return this.firstName + " " + this.lastName;
}
</pre><br />
這樣的問題是,每一個被活化的類別都需要進行一次上述動作(以下稱為成員繫結),如果你這類別會產生多個物件,就會造成匿名函式重複產生的問題(浪費記憶體)<br />
<a name='more'></a><br />
<br />
正確而有效的做法是進行 prototype 宣告,將類別定義修改如下<br />
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.fullName = function () { return this.firstName + " " + this.lastName; }
var p = new Person("Dino", "Wang");
var fullName = p.fullName(); // Workable!
</pre>此後每一個被活化的 Person 類別都自動具有 fullName 成員函式,不需要額外進行繫結<br />
<br />
值得一提的是,javascript 允許你對已經定義過的類別,附加成員,還且還會一併讓已經被活化的物件也具有這份"原型",例如:<br />
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">// 你可以為 String 擴充 trim() 功能
String.prototype.trim = function () { return this.replace(/^\s*|\s*$/g, ""); }
var name = " Dino ".trim();
// 為 Array 增加 append() 以模擬出 .NET Framework 效率較高的 StringBuilder
Array.prototype.append = function (val) { this.push(val); return this; }
var sb = [].append("1").append("2").append("3").join(" ");
</pre><br />
常用的 javascript 版本(1.2) 沒有常見的 class 的定義方式,因為它是 <a href="http://en.wikipedia.org/wiki/Prototype-based_programming">prototype-oriented</a><br />
<br />
<br />
NOTE: 關於 javascript 對比到一般物件導向語言的 繼承、類別成員、靜態成員 的方式,簡單整理如下:<br />
<pre type="syntaxhighlighter" class="brush: javascript; highlight: [];">// Constructor
function SuperClass() {}
// SuperClass behaviors
SuperClass.prototype.toString = function () {
return "I'm super class.";
}
// Constructor
function UserClass() {
SuperClass.apply(this, arguments); // Call constructor, like C# base(...)
}
// Inheritance
UserClass.prototype = new SuperClass;
// Data member
UserClass.prototype.property1 = 0;
UserClass.prototype.property2 = 0;
// Member function
UserClass.prototype.method1 = function () {}
UserClass.prototype.method2 = function () {}
// Static data member
UserClass.instances = 0;
UserClass.cache = [];
// Static member function
UserClass.staticMethod1 = function () {}
UserClass.staticMethod2 = function () {}
</pre>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-2778280659588351672010-09-15T01:56:00.006+08:002010-09-15T02:06:19.262+08:00設定 ASP.NET MVC 2 編譯時期檢查 View 的設定 MvcBuildViews 導致編譯失敗設定了 MvcBuildViews 可以得到<b>編譯時期檢查 View 正確性的好處</b>,於是從善如流趕緊在專案檔找到 MvcBuildViews 選項並設為 true。<br />
<pre type="syntaxhighlighter" class="brush: xml; highlight: [3];"><propertygroup>
...
<mvcbuildviews>true</MvcBuildViews>
...
</PropertyGroup>
</pre><br />
隨後編譯即出現:<br />
<br />
<b>無法載入型別 'System.Data.Entity.Design.AspNet.EntityDesignerBuildProvider' 錯誤</b>, <br />
<br />
結果追蹤到<a href="http://stackoverflow.com/questions/2762256/mvcbuildviews-true-with-entity-framework-in-asp-net-mvc-2" target="_blank">這篇</a>,才發現是因為專案使用 Entity Framework 的關係,在 web.config 中增加<br />
<pre type="syntaxhighlighter" class="brush: xml; highlight: [3];"><assemblies>
...
<add assembly="System.Data.Entity.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"></add>
</assemblies>
</pre><br />
即可順利編譯囉。Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-38192752715837766882010-04-27T01:00:00.021+08:002010-09-18T00:28:51.542+08:00在 DataTable 進行 Group by 並且取 Sum 的運算 (後續)在前一篇 <a href="http://dinowang.blogspot.com/2010/04/group-by-and-sum-values-in-datatable.html">在 DataTable 進行 Group by 並且取 Sum 的運算</a> 中,原以為已經提供可用的 solution 了,結果沒那麼順利<br />
<br />
儲存數字的 DataColumn 型態居然是 String (在資料庫中是 NVARCHAR),我無言了...<br />
<br />
維持著手上只有 DataTable 的條件,必須作一下轉型處理才能進行運算<br />
<a name='more'></a><br />
模擬資料來源的部分改為<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [4, 7, 8, 9, 10, 11];">DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("GroupKey", typeof(string));
dt.Columns.Add("Value", typeof(string));
ds.Tables.Add(dt);
dt.Rows.Add(new object[] { "P1", "10" });
dt.Rows.Add(new object[] { "P2", "40" });
dt.Rows.Add(new object[] { "P3", "50" });
dt.Rows.Add(new object[] { "P1", "20" });
dt.Rows.Add(new object[] { "P2", "5" });
</pre><br />
純 ADO.NET<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [2, 12];">// 提供轉型完成的欄位
dt.Columns.Add("Value1", typeof(int), "Convert(Value, 'System.Int32')");
// 分組並計算
DataTable grouped = dt.DefaultView.ToTable("DistinctTable",
true,
new[] { "GroupKey" });
ds.Tables.Add(grouped);
ds.Relations.Add("Relation1",
grouped.Columns["GroupKey"],
dt.Columns["GroupKey"]);
grouped.Columns.Add("SumValue", typeof(int), "Sum(Child.Value1)");
</pre>注意:試圖直接以 "Sum(Convert(Child.Value), 'System.Int32'))" 進行運算會於 runtime 時期出現 <b style="color: red;">Syntax error in aggregate argument: Expecting a single column argument with possible 'Child' qualifier.</b> 錯誤<br />
<br />
LINQ 的版本(相形之下又更簡單了)<br />
<pre type="syntaxhighlighter" class="brush: csharp; highlight: [7];">// 分組並計算
var grouped = from row in dt.AsEnumerable()
group row by row["GroupKey"] into g
select new
{
GroupKey = g.Key,
SumValue = g.Sum(p => int.Parse((string)p["Value"]))
};
</pre>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-77385804886567845292010-04-21T22:02:00.120+08:002010-09-18T00:29:29.288+08:00在 DataTable 進行 Group by 並且取 Sum 的運算今天幫忙同事以 DataTable 進行 Group by 並且取 Sum 的運算<br />
<br />
知道方法有很多,我就自己懂的部分,寫下純 ADO.NET 的作法,先以 DataTable 的 <a href="http://msdn.microsoft.com/en-us/library/system.data.dataview.totable.aspx" target="_blank">ToTable</a> 方法取得 distinct 過的群組鍵,接下來建立 <a href="http://msdn.microsoft.com/en-us/library/system.data.dataset.relations.aspx" target="_blank">Relation</a> 關連回原 DataTable,最後利用在群組鍵 DataTable 中增加自動計算欄位 (<a href="http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx" target="_blank">Expression 屬性</a>,透過 <a href="http://msdn.microsoft.com/en-us/library/632cdz7z.aspx" target="_blank">DataColumnCollection.Add</a> method 設定) 完成關聯記錄的計算<br />
<br />
見範例程式中 TestByPureAdoNet method 中<strike>寫得密密麻麻</strike>高亮的那幾行就是了<br />
<a name='more'></a><br />
我知道 <a href="http://msdn.microsoft.com/zh-tw/library/bb386977(VS.90).aspx" target="_blank">LINQ to DataSet</a> 能幫我做到一樣的事情,但是僅停留在<b>大概</b>知道怎麼做的階段,雖然同事的 .NET 2.0 環境也用不上,還是借機小練了一下,這才發現效能差距不小...<br />
<br />
LINQ 的作法可以從範例中的 TestByLinq method 看到<br />
<br />
<pre type="syntaxhighlighter" class="brush: c#; highlight: [37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 65, 66, 67, 68, 69, 70, 71];">using System;
using System.Linq;
using System.Data;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
Stopwatch stopWatch = new Stopwatch();
Console.WriteLine("Testing by pure ADO.NET...");
stopWatch.Start();
TestByPureAdoNet();
stopWatch.Stop();
Console.WriteLine(stopWatch.ElapsedMilliseconds + "ms");
Console.WriteLine("Testing by LINQ...");
stopWatch.Reset();
stopWatch.Start();
TestByLinq();
stopWatch.Stop();
Console.WriteLine(stopWatch.ElapsedMilliseconds + "ms");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
private static void TestByPureAdoNet()
{
for (int i = 0; i < 10000; i++)
{
DataSet ds = GetDataSet();
DataTable dt = ds.Tables[0];
// 分組並計算
DataTable grouped = dt.DefaultView.ToTable("DistinctTable",
true,
new[]{"GroupKey"});
ds.Tables.Add(grouped);
ds.Relations.Add("Relation1",
grouped.Columns["GroupKey"],
dt.Columns["GroupKey"]);
grouped.Columns.Add("SumValue",
typeof(int),
"Sum(Child.Value)");
foreach (DataRow dr in grouped.Rows)
{
var key = dr["GroupKey"];
var value = dr["SumValue"];
if (i == 0) System.Console.WriteLine(key + " = " + value);
}
}
}
private static void TestByLinq()
{
for (int i = 0; i < 10000; i++)
{
DataSet ds = GetDataSet();
DataTable dt = ds.Tables[0];
// 分組並計算
var grouped = from row in dt.AsEnumerable()
group row by row["GroupKey"] into g
select new
{
GroupKey = g.Key,
SumValue = g.Sum(p => (int)p["Value"])
};
foreach (var group in grouped)
{
var key = group.GroupKey;
var value = group.SumValue;
if (i == 0) System.Console.WriteLine(key + " = " + value);
}
}
}
// 產生測試資料集
public static DataSet GetDataSet()
{
DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("GroupKey", typeof(String));
dt.Columns.Add("Value", typeof(int));
ds.Tables.Add(dt);
dt.Rows.Add(new object[] { "P1", 10 });
dt.Rows.Add(new object[] { "P2", 40 });
dt.Rows.Add(new object[] { "P3", 50 });
dt.Rows.Add(new object[] { "P1", 20 });
dt.Rows.Add(new object[] { "P2", 5 });
return ds;
}
}
</pre><br />
最後既然 method 都實做了,當然要來量測一下效能,在 main 中使用 <a href="http://msdn.microsoft.com/zh-tw/library/system.diagnostics.stopwatch(VS.90).aspx" target="_blank">Stopwatch</a> 取得測試 method 的執行花費時間,每個測試 method 中均反覆執行 10000 次以放大效能表現數值,發現以 LINQ to DataSet 進行查詢的效能平均約為純 ADO.NET 方式的 5~6 倍!<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/group-by-and-sum-values-in-datatable/test-result.png?attredirects=0"/><br />
<br />
不過這種作業要反覆執行這麼多次的機會並不高,如果僅執行一次而言,initial cost + 執行時間,兩者相當,以我測試環境下測得均為 3ms<br />
<br />
感嘆 LINQ 果然是好物啊!見到發明人 <a href="http://en.wikipedia.org/wiki/Anders_Hejlsberg" target="_blank">Anders Hejlsberg</a> 快拜就對了!<br />
<br />
參考資料:<br />
<a href="http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx" target="_blank">101 LINQ Samples</a>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com1tag:blogger.com,1999:blog-8962867040946426762.post-48162151122316721642010-04-12T22:15:00.030+08:002010-10-10T23:21:35.376+08:00一些 QR Code 開放原始碼資源<h3>編碼 與 解碼</h3><ul><li>QR Code for .NET Framework 支援 Windows Mobile 平台,提供範例程式碼,至 <a href="http://www.twit88.com/home/" target="_blank">twit88</a> 或 <a href="http://www.codeproject.com/KB/cs/qrcode.aspx" target="_blank">CodeProject</a> 下載</li>
<li><a href="http://qrcode.sourceforge.jp/" target="_blank">Open Source QR Code Library</a> for Java,專案網頁於 SourceForge</li>
<li><a href="http://code.google.com/p/zxing/" target="_blank">ZXing project </a> for Java / C++ / ActionScript 支援 Android 與 iPhone 平台,專案網頁於 Google Code</li>
<li><a href="http://www.pedemonte.eu/pyqr/index.py/pyqrhome" target="_blank">PyQrCodec</a> for Python,其實 PyQrCodec 算是 wrapper,因為它依賴一群 DLL 元件,主要呼叫 <b>qr_codec.dll</b> 中的 API,至於 qr_codec.dll 的原始碼則未包含於此專案中</li>
<li><a href="http://www.d-project.com/qrcode/" target="_blank">d-project</a> for ActionScript 3 / <u>JavaScript</u> / PHP / Java,專案網頁於 Google Code<br />
(JavaScript 版是有點出乎意料到 XD)</li>
</ul><a name='more'></a><br />
<h3>僅提供編碼</h3><ul><li><a href="http://megaui.net/fukuchi/works/qrencode/index.en.html" target="_blank">libqrencode</a> for C</li>
<li><a href="http://code.google.com/intl/zh-TW/apis/chart/docs/gallery/qr_codes.html" target="_blank">Google Chart Tool</a>, RESTful<br />
<br />
<img src="http://chart.apis.google.com/chart?chs=150x150&cht=qr&chl=http%3A%2f%2fdinowang.blogspot.com%2f&choe=UTF-8"/><br />
</li>
</ul><br />
關鍵字:<br />
QR Code, QRCode, 2D BarcodeAnonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com3tag:blogger.com,1999:blog-8962867040946426762.post-67269570343100448902010-04-10T23:51:00.013+08:002010-09-18T00:30:02.252+08:00初探網頁弱點偵測工具 Burp Suite初次接觸 Burp Suite 這套 web 漏洞檢測工具,覺得真是簡單易用,當你懂得一些 HTTP 的原理與特性,不必寫程式就可以進行漏洞測試 (說是攻擊測試,好像更加準確)<br />
<br />
分析 HTTP 的工具中,我其實比較常用 <a href="http://www.fiddlertool.com/" target="_blank">Fiddler</a> 和 <a href="http://www.wireshark.org/" target="_blank">WireShark</a>,它們同樣都可以捕捉到 HTTP 封包 (WireShark 用途更廣),在 Web 偵錯時非常的好用,你可以取得非常豐富的 HTTP 資訊<br />
<br />
相形之下,<span style="color: red;">Burp Suite 就是為了攔截、修改 HTTP requests 的用途而誕生</span> (切記是學術、是研究!)<br />
<br />
這裡我打算紀錄第一次使用的經驗,因為 Brup Suite 是以 Java 寫成的軟體,所以得先確認有 <a href="http://www.java.com/" target="_blank">JRE</a> 環境<br />
<a name='more'></a><br />
以下的說明,基於 Burp Suite v1.3 的操作界面<br />
<br />
<a href="http://portswigger.net/suite/download.html" target="_blank">下載 Brup Suite</a> 之後,先解壓縮,不需要安裝直接執行 suite.bat 就可以<br />
<br />
確認一下 proxy / intercept 頁籤中的 intercept is on (小抱怨一下,我討厭這個按鈕的 UI... XD)<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/1_intercept_is_on.png?attredirects=0"/><br />
<br />
確認一下 proxy / option 頁籤中 port 的預設值有沒有與你自己系統的其他服務衝突,需不需要修改<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/2_default_proxy_port.png?attredirects=0"/><br />
<br />
在 browser 中設定好 proxy,預設是如上圖的 127.0.0.1 埠 8080<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/3_set_browser_proxy.png?attredirects=0"/><br />
<br />
一切就緒,進入到 browser 打開任何一個網頁,此時 Burp Suite 立刻攔截下 HTTP request<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/4_browse_will_cause_interception.png?attredirects=0"/><br />
<br />
在視窗的下半部所有的東西你都能改,input maxLength 限制不到你,javascript validation 限制不到你,連改 cookie 都粉方便吶<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/6_intercept_and_modification_in_raw.png?attredirects=0"/><br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/5_intercept_and_modification_in_param.png?attredirects=0"/><br />
<br />
改完之後按下 forward 就可以把 request 真的丟出去,而且 server 端根本不知道 request 被動過了手腳 <br />
<br />
如果不再作監聽、修改,記得把 proxy / intercept 頁籤中的 intercept 切換為 off,不然持續跳出的攔截會挺煩的<br />
<br />
<img src="http://sites.google.com/site/dinowang64/blog/first-use-of-burp-suite/7_intercept_is_off.png?attredirects=0"/><br />
<br />
Burp Suite 的功能相當的多,做下第一次接觸的紀錄,並感嘆 web 安全的領域真的需要下苦功...Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com1tag:blogger.com,1999:blog-8962867040946426762.post-15820257561563408952010-04-06T17:39:00.009+08:002010-10-10T23:22:11.988+08:00在 SQL Server 中將欄位值重設為 Null在 SQL Server Management Studio 中,要修改 TABLE 某列的欄位值是開發期間經常會發生的事情,但是你有遇到過需要將欄位重設為 Null 嗎?<br />
<br />
過去我遇到這樣的情況時,都只能用很爛的步數,那就是 <b>Copy & paste</b><br />
<br />
就這樣:將作用中的儲存格移動到含有 Null 值的欄位上 copy,再移到要設為 Null 的欄位上 paste,搞定<br />
<a name='more'></a><br />
哈!像是刪掉一整列的<strike>傻</strike>事我是做不出來滴 XD<br />
<br />
不過... 今天我進化了!把欄位值清空為 Null 更簡單的方法是按下 <b style="color: red;">Ctrl+0</b> (零)<br />
<br />
<img src="http://1941364553817855237-a-1802744773732722657-s-sites.googlegroups.com/site/dinowang64/blog/set-field-to-null-in-sql-server-1/after.png" /><br />
<br />
媽呀,當時猴傻猴天真... 原來夭壽簡單Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com1tag:blogger.com,1999:blog-8962867040946426762.post-70813365583788435472010-04-05T00:48:00.010+08:002010-11-30T09:22:56.035+08:00分享 NPlurk - The Plurk API .NET Wrapper using C#嗯... 去年底完成之後,就一直擱置著,想到時間都花了,多可惜,因此想讓有需要的人可以取用<br />
<br />
這個 .NET Wrapper 是用 C# 寫完的,<strike>引用了 CodePlex 上的 <a href="http://json.codeplex.com/Wikipage" target="_blank">Json.NET</a> 組件</strike> (新版本已改用 .NET 內建的 JavaScriptSerializer)<br />
<br />
實作的過程中,沒有太深奧的技巧,全因原生的 Plurk API 設計結構非常簡單,剩下的都只是原始 API 對應文件的苦工<br />
<a href="http://www.codeplex.com/" target="_blank"><img src="http://i1.codeplex.com/Images/v16429/logo-home.png" style="float: right;"/></a><br />
我把它發佈到 <a href="http://www.codeplex.com/" target="_blank">CodePlex</a> 上了,這裡是專案網頁 <a href="http://nplurk.codeplex.com/" target="_blank">NPlurk</a><br />
<br />
Enjoy!Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com10tag:blogger.com,1999:blog-8962867040946426762.post-40190325078201140062010-04-04T20:59:00.022+08:002010-10-10T23:20:56.954+08:00動態 CSS 選單在瀏覽器中顯示異常週五有同事遇到了網頁程式中的動態選單無法正常顯示的問題,被 art 提供的 flash 元件遮住大半,仍舊是典型的 CSS menu 顯示異常的問題<br />
<br />
處理過太多次了,每次都得花我很多<strike>口水</strike>精神解釋,這次乾脆著文一篇,下次好直接參考使用<br />
<br />
一般 CSS menu 在顯示上會遇到以下的問題,稍作整理<br />
<ul><li>被 flash 物件所覆蓋,這種情況在 Firefox 中沒有發現,其他常見瀏覽器幾乎都會</li>
<li>被 <select /> 網頁元素所覆蓋,目前這種情況只剩下<strike>顧人怨的</strike> IE6 會發生</li>
</ul><br />
以下程式碼用來試驗各種瀏覽器不同<b>遮蔽</b>的情況<a name='more'></a><br />
<pre type="syntaxhighlighter" class="brush: html"><html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<style>
.menu {
position: absolute;
border: solid 1px gray;
background: #f0f0f0;
padding: 3px 10px;
display: none;
}
.menu ul {
list-style: none;
margin: 0px;
padding: 0px;
max-width: 200px;
}
.menu a {
line-height: 150%;
text-decoration: none;
}
.menu a:hover {
background: #e0e0e0;
}
</style>
<script type="text/javascript">
var vars = {}, params = {};
swfobject.embedSWF('http://images.tv.adobe.com/swf/player.swf', 'MSH_MashupViewer_87602', '400', '121', '8',
'http://dilbert.com/swf/v1/express_install.swf', vars, params);
$(document).ready(function () {
var $menu = $(".menu_pad");
var $div = $menu.find("div.menu");
$menu.hover(
function () {
var pos = $menu.position();
var dim = { top: pos.top + $menu.outerHeight(), left: pos.left };
$div.css(dim).show();
},
function () {
$div.hide();
});
});
</script>
</head>
<body>
<div>
<span class="menu_pad">
Menu
<div class="menu">
<ul>
<li><a href="javascript:">New</a></li>
<li><a href="javascript:">Open</a></li>
<li><a href="javascript:">Exit</a></li>
</ul>
</div>
</span>
<br/>
<select>
<option value="1">option 1</option>
<option value="2">option 2</option>
<option value="3">option 3</option>
</select>
<br/>
<div id="MSH_MashupViewer_87602"></div>
</div>
</body>
</html>
</pre>在常見瀏覽器的測試顯示結果如下表列出<br />
<table><tr> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/ie6.png?attredirects=0"/><br />
IE 6 <strike>這種結果,難怪連狗都嫌 XD</strike><br />
</td> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/ie7.png?attredirects=0"/><br />
IE 7<br />
</td> </tr>
<tr> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/ie8.png?attredirects=0"/><br />
IE 8<br />
</td> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/ie9preview.png?attredirects=0"/><br />
IE 9 preview<br />
</td> </tr>
<tr> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/firefox3.6.2.png?attredirects=0"/><br />
Firefox 3.6.2<br />
</td> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/safari4.0.5.png?attredirects=0"/><br />
Safari 4.0.5<br />
</td> </tr>
<tr> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/opera10.51.png?attredirects=0"/><br />
Opera 10.51<br />
</td> <td><br />
<img src="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/chrome5.0.360.4dev.png?attredirects=0"/><br />
Chrome 5.0.360.4 Dev<br />
</td> </tr>
</table><br />
在 flash 的部分,可以在 Adobe 官網上找到解決方式,請參考 <a href="http://kb2.adobe.com/cps/155/tn_15523.html" target="_blank">Flash content displays on top of all DHTML layers</a> 的指引<b>加入 wmode 參數並指定為 transparent</b><br />
<br />
在 <select /> 的部分,則可以用一份透明的 iframe 來幫忙遮住強勢的 select 元素,而且它得剛好墊在需要被保護的顯示元件之下<br />
<br />
修改後的內容如下,反白的部分是小小的調整<br />
<pre type="syntaxhighlighter" class="brush: html; highlight: [26, 27, 28, 29, 30, 31, 32, 33, 34, 37, 43, 44, 50, 51, 52, 56];"><html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<style>
.menu {
position: absolute;
border: solid 1px gray;
background: #f0f0f0;
padding: 3px 10px;
display: none;
}
.menu ul {
list-style: none;
margin: 0px;
padding: 0px;
max-width: 200px;
}
.menu a {
line-height: 150%;
text-decoration: none;
}
.menu a:hover {
background: #e0e0e0;
}
.glass {
position: absolute;
filter: alpha(opacity=0);
-moz-opacity: 0;
opacity: 0;
width: 100%;
height: 100%;
display: none;
}
</style>
<script type="text/javascript">
var vars = {}, params = { wmode: 'transparent' };
swfobject.embedSWF('http://images.tv.adobe.com/swf/player.swf', 'MSH_MashupViewer_87602', '400', '121', '8',
'http://dilbert.com/swf/v1/express_install.swf', vars, params);
$(document).ready(function () {
var $menu = $(".menu_pad");
var $glass = $("<iframe frameBorder='0' class='glass'></iframe>");
var $div = $menu.find("div.menu").before($glass);
$menu.hover(
function () {
var pos = $menu.position();
var dim = { top: pos.top + $menu.outerHeight(), left: pos.left };
$div.css(dim).show();
dim.width = $div.outerWidth();
dim.height = $div.outerHeight();
$glass.css(dim).show();
},
function () {
$div.hide();
$glass.hide();
});
});
</script>
</head>
<body>
<div>
<span class="menu_pad">
Menu
<div class="menu">
<ul>
<li><a href="javascript:">New</a></li>
<li><a href="javascript:">Open</a></li>
<li><a href="javascript:">Exit</a></li>
</ul>
</div>
</span>
<br/>
<select>
<option value="1">option 1</option>
<option value="2">option 2</option>
<option value="3">option 3</option>
</select>
<br/>
<div id="MSH_MashupViewer_87602"></div>
</div>
</body>
</html>
</pre>這樣一來,幾乎常見瀏覽器的顯示情況就都在控制內了<br />
<br />
<a href="https://sites.google.com/site/dinowang64/blog/css_menu_display_abnormal/css_menu_display_abnormal.zip?attredirects=0&d=1">完整的範例程式碼下載</a>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-54310860764269457402010-04-02T21:46:00.007+08:002010-10-10T23:23:27.087+08:00網頁的多重捲動(Parallax Scrolling)效果今天在號稱 Front-End Engineer 的 <a href="http://james.padolsey.com/about/" target="_blank">James Padolsey</a> 大大 <a href="http://james.padolsey.com/" target="_blank">Blog</a> 中挖寶時,被捲動的網頁效果吸引住了<br />
<br />
起初以為是老花,但是以我20多年前打電玩的經驗,和一天使用超過18小時卻被醫生嫌沒有近視的火眼金睛,鑑定為<b>多重捲軸</b>確診案例<br />
<br />
James 的網頁確實以兩個層次捲動著 <strike>搞什麼阿</strike><br />
<br />
因為<strike>只</strike>共有兩層,當下就想背景一定是在 body 的 CSS 中以 background-image 和動態 background-position 指定(控制 y 軸)兜出來的效果<a name='more'></a><br />
<br />
在<a href="http://james.padolsey.com/wp-content/themes/james.padolsey/m.js" target="_blank">啟始 js</a>中確實找到這一段<br />
<pre type="syntaxhighlighter" class="brush: javascript">(function(){
var a = document.body, e = document.documentElement;
$(window)
.unbind("scroll")
.scroll(function(){
var b = Math.max(e.scrollTop, a.scrollTop) / 8;
a.style.backgroundPosition = "0px " + -b + "px";
});
})();
</pre>CSS 則為<br />
<pre type="syntaxhighlighter" class="brush: css">body {
background: #222 url(images/bg.png) fixed;
font-size: 0.8em;
font-family: Verdana, Helvetica, Arial, Sans-Serif;
}
</pre>這就是了!<br />
<br />
瞭解了原理後,就想自己寫個 jQuery 來用,但是不甘心的想到,偌大的網路世界不會只有我這<strike>庸才</strike>傢伙在乎這個效果吧,再搜尋看看!<br />
<br />
果然又陸續找到 <a href="http://inner.geek.nz/javascript/parallax/" target="_blank">Parallax Backgrounds, a multi–layered javascript experiment</a><br />
<br />
和 <a href="http://blog.themeforest.net/tutorials/create-a-funky-parallax-background-effect-using-jquery/" target="_blank">Create a Funky Paralla Background Effect using jQuery</a><br />
<br />
不列舉了,<a href="http://www.google.com/search?q=Parallax+Backgrounds+jQuery" target="_blank">搜尋看看更多</a><br />
<br />
我真的蠻喜歡這網頁效果<br />
<br />
好了!收工了!反正都有人搞定了 XD<br />
<br />
<br />
參考資料:<br />
<a href="http://en.wikipedia.org/wiki/Parallax_scrolling" target="_blank">Wikipedia - Parallax Scrolling</a><br />
<br />
效果名稱:<br />
Parallax Scrolling, Parallax Backgrounds 或 MultiplaneAnonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-87365644885826107892010-03-30T23:58:00.011+08:002010-10-10T23:23:56.533+08:00C# 4.0 dynamic 關鍵字與 ExpandoObject昨天翻了一下 <a href="http://books.google.com/books?id=L6Z3TdQigE0C&printsec=frontcover&hl=zh-TW&source=gbs_v2_summary_r&cad=0" target="_blank">Apress 的 Introducing .NET 4.0</a>,看到動態語言特性的部分,一個很面熟的 expando 字眼出現在面前,它是 <a href="http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject(VS.100).aspx" target="_blank">ExpandoObject</a>。<br />
<br />
不清楚 expando 一詞多久以前就出現了,但是如果是 JavaScript 就不能不提 expando 特性,它讓任何一個物件能隨時隨地的被賦予新成員 (包含屬性和函式)<br />
<pre type="syntaxhighlighter" class="brush: javascript">// JavaScript
var data = new Object();
data.name = "Dino";
data.sex = "Male";
</pre>另一種 JavaScript 等價程式碼 (<a href="http://www.json.org/" target="_blank">JSON</a> 表示法)<br />
<pre type="syntaxhighlighter" class="brush: javascript">// JavaScript
var data = { name: "Dino", sex: "Male" };
</pre>程式執行下來 data 含有 name、sex 這兩個屬性,並且值也被賦予了,我不必先為他產生類別定義,多麼簡單直覺!<br />
<a name='more'></a><br />
回到主題,在 ExpandoObject 就有這樣的精神,在 MSDN 中你能夠找到類似的範例程式:<br />
<pre type="syntaxhighlighter" class="brush: csharp">// C# 4.0
dynamic sampleObject = new ExpandoObject();
sampleObject.Name = "Dino";
sampleObject.Sex = "Male";
</pre>(呵,懂得 JavaScript 的設計師應該馬上就能會心一笑吧)<br />
<br />
為了支援在程式碼中存取不明確的類別成員而不被編譯器判出局,C# 增加了 <a href="http://msdn.microsoft.com/en-us/library/dd264736(VS.100).aspx" target="_blank">dynamic</a> 關鍵字,用來使編譯器繞過型別檢查。<br />
<br />
不過 dynamic 的用途可更大著,它在其他方面有更多貢獻。<br />
<br />
必須要注意的是,以 C# 3.0 可以達成結果看似相同,但是特性卻截然不同的另一種方式:<a href="http://msdn.microsoft.com/zh-tw/library/bb397696.aspx" target="_blank">匿名型別</a>,如下的程式碼,可以產生與前述 C# 4.0 <b>接近</b>的結果<br />
<pre type="syntaxhighlighter" class="brush: csharp">// C# 3.0
var sampleObject = new { Name = "Dino", Sex = "Male" };
</pre>不同的地方在於以 ExpandoObject new 出來的新物件還能夠陸續的增加新成員,但是 C# 3.0 的 var sampleObject 卻是一個實實在在的靜態類別,不能、也無法增加新成員。<br />
<br />
以 ExpandoObject 產出的實體,要成為 ASP.NET 網頁前端 JSON 的資料來源存取方式,可真是更加簡單了!<br />
<br />
<br />
最後要推一下,黃先生這篇 <a href="http://www.dotblogs.com.tw/code6421/archive/2010/01/23/13225.aspx" target="_blank">C# 4.0 New Feature : Dynamic Programming</a>,介紹得可真棒!Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-67853027052550733552010-03-28T22:52:00.012+08:002010-10-10T23:24:19.032+08:00Lazy Load - jQuery plugin for lazy loading images之前在瀏覽一些網站時,發現有些網站圖片內容載入好像稍微慢了一些,像是 <a href="http://jquerylist.com/" target="_blank">The Ultimate jQuery List</a>。<br />
<br />
仔細的觀察一下行為,就會查覺到這些網站的圖片載入的速度其實並不慢,只是<b style="font-size: larger;"><u>圖片會在捲動到可視範圍內</u></b>才開始載入。<br />
<br />
這麼作真適合版面較長的網頁,可以舒緩伺服器端的瞬間吞吐量,能夠增加效率而且並不難,已經有現成的 <a href="http://www.appelsiini.net/projects/lazyload" target="_blank">Lazy Load Plugin for jQuery</a> 能用,真是一整個開心吶。<br />
<br />
簡介一下使用方法,除了 jQuery,你還需要先<a href="http://www.appelsiini.net/download/jquery.lazyload.mini.js">下載 jquery.lazyload.js</a>,並且在網頁中把它們都載入。<a name='more'></a><br />
<pre type="syntaxhighlighter" class="brush: html"><script src="jquery.js" type="text/javascript"></script>
<script src="jquery.lazyload.js" type="text/javascript"></script>
</pre>然後在網頁中加入啟用的 script 段,就這樣<br />
<pre type="syntaxhighlighter" class="brush: javascript">$("img").lazyload();
</pre>大功告成!<br />
<br />
感謝 Mika Tuupola 的 <a href="http://www.appelsiini.net/projects/lazyload" target="_blank">Lazy Load Plugin for jQuery</a>,想看看效果的話可以到先前提到的 <a href="http://jquerylist.com/" target="_blank">The Ultimate jQuery List</a>。Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-36616130693048458602010-03-27T19:30:00.005+08:002010-03-28T21:37:28.332+08:00在 Blogger 中使用 SyntaxHighlighter可參考這篇文章:<a href="http://blog.cartercole.com/2009/10/awesome-syntax-highlighting-made-easy.html" target="_blank">Awesome code syntax highlighting made easy</a><br />
<br />
<a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter" target="_blank">Syntax Highlighter 網站</a>Anonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0tag:blogger.com,1999:blog-8962867040946426762.post-78681574327306712552010-03-25T23:47:00.039+08:002010-10-10T23:24:45.152+08:00C# 排列組合練習, 使用 extension method and yield return此例用飲料<b>口味</b>、<b>甜度</b>、<b>冰塊</b>作為測試情境為求容易理解。<br />
<br />
測試資料集 optionValueSet 型態可為 Dictionary<string, List<string>> 或 Dictionary<string, string[]> 等,主要是因為 extension method 設計對象為 IDictionary<string, IEnumerable<string>>。<br />
<br />
主程式 (Program.cs)<a name='more'></a><br />
<script type="syntaxhighlighter" class="brush: csharp; wrap-lines: false;">
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace Permutation
{
class Program
{
static void Main(string[] args)
{
var optionValueSet = new Dictionary<string, IEnumerable<string>>()
{
{"口味", new[] {"烏龍綠", "檸檬紅茶", "仙草蜜", "百香綠", "梅子綠"}},
{"甜度", new[] {"正常", "少糖", "無糖"}},
{"冰塊", new[] {"正常", "少冰", "無冰"}}
};
int count = 0;
foreach (NameValueCollection nvc in optionValueSet.Permutation())
{
count++;
Console.WriteLine(nvc.TextContent());
}
Console.WriteLine("共 " + count + " 種組合");
Console.ReadKey();
}
}
}
</script><br />
擴充方法類別 (ExtensionMethods.cs)<br />
<script type="syntaxhighlighter" class="brush: csharp; wrap-lines: false;">
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
namespace Permutation
{
public static class ExtensionMethods
{
public static IEnumerable< NameValueCollection> Permutation
(this IDictionary<string, IEnumerable<string>> optionValueSet)
{
var candicateKeys = new Stack<string>(optionValueSet.Keys);
foreach (NameValueCollection nvc in optionValueSet.Permutation(candicateKeys))
{
yield return nvc;
}
}
private static IEnumerable< NameValueCollection> Permutation
(this IDictionary<string, IEnumerable<string>> optionValueSet, Stack<string> candicateKeys)
{
string key = candicateKeys.Pop();
IEnumerable<string> values = optionValueSet[key];
if (candicateKeys.Count > 0)
{
foreach (NameValueCollection nvc in optionValueSet.Permutation(candicateKeys))
{
foreach (string value in values)
{
nvc[key] = value;
yield return nvc;
}
}
}
else
{
foreach (string value in values)
{
NameValueCollection nvc = new NameValueCollection();
nvc[key] = value;
yield return nvc;
}
}
}
public static string TextContent(this NameValueCollection nvc)
{
StringBuilder sb = new StringBuilder();
string sep = "";
foreach (string key in nvc.Keys)
{
sb.Append(sep).Append(key).Append("=").Append(nvc[key]);
sep = ", ";
}
return sb.ToString();
}
}
}
</script><br />
實務上我是將此排列組合應用於 <a href="http://msdn.microsoft.com/zh-tw/library/system.net.webclient(VS.80).aspx" target="_blank">System.Net.WebClient</a> 並利用 <a href="http://www.codeplex.com/htmlagilitypack" target="_blank">HTML Agility Pack</a> 來分析網頁內容的程式上,作為 Request 的組合處理,抓取網頁資料並重組為資料庫。<br />
<br />
話說,我忘記 <a href="http://www.codeplex.com/" target="_blank">CodePlex</a> 這個珍貴的寶庫了,一<a href="http://www.codeplex.com/site/search?query=Permutations&ac=8" target="_blank">搜尋</a>果然有類似的東西。<br />
<br />
關鍵字:<br />
PermutationsAnonymoushttp://www.blogger.com/profile/02885041787200129288noreply@blogger.com0