CopySourceAsHtml 是個很棒的工具,可用來輔助製作程式文件。它是一個 Visual Studio 的 add-in,安裝好之後,只要在 VS2005 的編輯畫面中點滑鼠右鍵,再選「Copy As HTML...」,就能將選取的程式碼轉成 HTML 並複製到剪貼簿。這對於製作程式文件、在網站上分享程式碼來說,真是很方便的工具。
可惜的是,我使用之後發現中文字無法正確處理;精確的說,應該是無法正確處理 Unicode 字元。因此我自己動手將這個部份的 bugs 修正了(開放原碼萬歲!)。為了示範成果,我將自己的類別庫中的字串工具類別貼上來,附在文後。需要這個修正版的朋友,可以直接下載附件回去,然後照以下步驟進行:
- 下載 CopySourceAsHtml 2.0.0 Installer。直接執行裡面的安裝程式即可(安裝前請先關閉 VS2005) 。
- 將本文附件的壓縮檔解開,把裡面的 bin\ 目錄下的 CopySourceAsHtml.dll 和 CopySourceAsHtml.pdb 覆蓋掉原本安裝的檔案。原本這兩個檔案是安裝在本機的 Documents and Settings\<USER>\My Documents\Visual Studio 2005\Addins\ 目錄下。
- 完成!開啟 VS2005 測試看看:開啟一個程式檔,選取文字,點右鍵,「Copy As HTML...」,再貼到某個網頁中看看轉出來的結果。
要特別說明的是,在修正 bug 時,主要是處理 RTF 字串的剖析,由於這部分我還是第一次處理,因此是一邊看 RTF spec,一邊寫單元測試來逐一測試各種中文字的編碼情況。不保證 100% 沒問題喔!若您有發現問題,可回應此文,或者您也可以自己動手修改,完整原始碼我都附上了(只要搜尋關鍵字 "Tsai" 就能找到我修改的部份)。
2006-10-24 更新:
使用 Visual Studio 2005 中文版的朋友還必須修正 Connect.cs,可參考 Hikari 提供的修正:
194 commandBars = (CommandBars)this.application.CommandBars;
195 editPopup = (CommandBarPopup)commandBars["MenuBar"].Controls["編輯(&E)"];
196
197 copyIndex = FindCopyOnCommandBar("編輯(&E)") + 1;
感謝 Hikari!
註:這部份的修改若能改成支援多國語言的方式,以自動支援兩種版本的 VS2005,就更好了。若有人有興趣修改,可參考 Connect.cs 中的 "Resource.*" 的程式碼,以及 Resources.resx 的內容。改完的話希望也能寄一份給我,功德無量 :)
============================================================
以下是我的字串工具類別,有些地方顯示成表情圖案,是因為程式碼剛好與此部落格的表情圖案碼相同。
1 using System;
2 using System.Text;
3 using System.Text.RegularExpressions;
4 using System.Web;
5 using System.Security.Cryptography;
6
7 namespace Huanlin.Helpers
8 {
9 /// <summary>
10 /// 字串處理工具類別。
11 /// </summary>
12 public sealed class StrHelper
13 {
14 private StrHelper()
15 {
16 }
17
18 /// <summary>
19 /// Append a slash character '\' to string.
20 /// </summary>
21 /// <param name="input">輸入字串。</param>
22 /// <returns>輸出字串。</returns>
23 public static string AppendSlash(string input)
24 {
25 if (input == null)
26 return @"\";
27 if (input.EndsWith("/") || input.EndsWith("\\"))
28 return input;
29 return input + "\\";
30 }
31
32 /// <summary>
33 /// Remove the last slash character.
34 /// </summary>
35 /// <param name="input">輸入字串。</param>
36 /// <returns>輸出字串。</returns>
37 public static string RemoveLastSlash(string input)
38 {
39 if (input == null)
40 return "";
41 if (input.EndsWith("/") || input.EndsWith("\\"))
42 return input.Remove(input.Length-1, 1);
43 return input;
44 }
45
46 /// <summary>
47 /// 移除任何一個指定的字元。
48 /// </summary>
49 /// <param name="input">輸入字串。</param>
50 /// <param name="anyOf">要移除的字元。</param>
51 /// <returns>輸出字串。</returns>
52 public static string RemoveAny(string input, char[] anyOf)
53 {
54 if (String.IsNullOrEmpty(input))
55 return input;
56
57 int i;
58 while (true)
59 {
60 i = input.IndexOfAny(anyOf);
61 if (i < 0)
62 break;
63 input = input.Remove(i, 1);
64 }
65 return input;
66 }
67
68 /// <summary>
69 /// 移除換行字元(\n) 和(\r)。
70 /// </summary>
71 /// <param name="input">輸入字串。</param>
72 /// <returns>輸出字串。</returns>
73 public static string RemoveNewLines(string input)
74 {
75 return StrHelper.RemoveNewLines(input, false);
76 }
77
78 /// <summary>
79 /// 移除換行字元(\n) 和(\r)。
80 /// </summary>
81 /// <param name="input">輸入字串。</param>
82 /// <param name="addSpace">若為true,則把換行符號替換成空白字元。</param>
83 /// <returns>輸出字串。</returns>
84 public static string RemoveNewLines(string input,
85 bool addSpace)
86 {
87 string replace = string.Empty;
88 if (addSpace)
89 replace = " ";
90
91 string pattern = @"[\r|\n]";
92 Regex regEx = new Regex(pattern, RegexOptions.Multiline);
93
94 return regEx.Replace(input, replace);
95 }
96
97 /// <summary>
98 /// 移除空白字元(包括全形空白)。
99 /// </summary>
100 /// <param name="input">輸入字串。</param>
101 /// <param name="fullShapeSpaces">是否連全形空白也一併刪除。</param>
102 /// <returns>輸出字串。</returns>
103 public static string RemoveSpaces(string input, bool fullShapeSpaces)
104 {
105 string replace = string.Empty;
106 string pattern = @"[ ]";
107 if (fullShapeSpaces)
108 {
109 pattern = @"[ | ]";
110 }
111
112 Regex regEx = new Regex(pattern, RegexOptions.Multiline);
113
114 return regEx.Replace(input, replace);
115 }
116
117
118 /// <summary>
119 /// 檢驗身分證號碼的正確性。
120 /// 驗證規則:
121 ///
122 /// 身份證統一編號共計有10 位,其中第一位為英文字母,後共有九個數字;
123 /// 而最後一位數字為檢查碼( Check Digit ) ,表示如下表:
124 ///
125 /// L1 D2 D2 D3 D4 D5 D6 D7 D8 D9
126 ///
127 /// L1: 英文字母, 代表出生地的縣/市代號
128 /// D2: 性別,1=男, 2=女
129 /// D9: 檢查碼
130 ///
131 /// L1 對照表:
132 ///
133 /// 字母A B C D E F G H J K L M N
134 /// 代號10 11 12 13 14 15 16 17 18 19 20 21 22
135 /// -------------------------------------------
136 /// 字母P Q R S T U V X Y W Z I O
137 /// 代號23 24 25 26 27 28 29 30 31 32 33 34 35
138 ///
139 /// 令其十位數為X1 ,個位數為X2 ;( 如A:X1=1 , X2=0 )
140 ///
141 /// 依其公式計算結果:
142 /// Y= X1 + 9*X2 + 8*D1 + 7*D2 + 6*D3 + 5*D4 + 4*D5 + 3*D6 + 2*D7 +
143 /// D8 + D9
144 ///
145 /// 假如Y能被10 整除,則表示該身份證號碼為正確,否則為錯誤。即如以10 為
146 /// 模數,檢查號碼為( 10 - Y- D9 ) / 10 的餘數,如餘數為0 時,則檢查碼
147 /// 為0 。
148 ///
149 /// </summary>
150 /// <param name="input"></param>
151 /// <returns></returns>
152 public static bool CheckIdno(string idno)
153 {
154 int[] letter_weight =
155 {
156 // A B C D E F G H I J K L M N O P Q R
157 10, 11, 12, 13, 14, 15, 16, 17, 34, 18, 19, 20, 21, 22, 35, 23, 24, 25,
158 26, 27, 28, 29, 32, 30, 31, 33
159 // S T U V W X Y Z
160 };
161
162 int i;
163 int[] D = new int[9]; // 1..9
164 int sum;
165
166 if (idno.Length != 10)
167 return false;
168 idno = idno.ToUpper();
169 if (!Char.IsLetter(idno[0]) || (idno[1] != '1' && idno[1] != '2'))
170 return false;
171 for (i = 1; i < 10; i++)
172 {
173 if (!Char.IsDigit(idno, i))
174 return false;
175 }
176
177 for (i = 0; i < 9; i++) // 1..9
178 {
179 D = Int32.Parse(idno.Substring(i+1, 1));
180 }
181
182 i = letter_weight[idno[0]-'A'];
183 sum = (i / 10) + (i % 10) * 9;
184 sum = sum + 8 * D[0] + 7 * D[1] + 6 * D[2] + 5 * D[3]
185 + 4 * D[4] + 3 * D[5] + 2 * D[ + D[7] + D[;
186 return (sum % 10 == 0);
187 }
188
189 /// <summary>
190 /// 判斷字串值是否為數字。
191 /// </summary>
192 /// <param name="input"></param>
193 /// <returns></returns>
194 public static bool IsDigit(string s)
195 {
196 try
197 {
198 long num = Convert.ToInt64(s);
199 return true;
200 }
201 catch
202 {
203 return false;
204 }
205 }
206
207 /// <summary>
208 /// 將字串轉成位元組陣列。
209 /// </summary>
210 /// <param name="str"></param>
211 /// <returns></returns>
212 public static byte[] StrToByteArray(string str)
213 {
214 System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
215 return encoding.GetBytes(str);
216 }
217
218 /// <summary>
219 /// 將字串轉成位元組陣列。
220 /// </summary>
221 /// <param name="str">輸入字串。</param>
222 /// <param name="enc">編碼。</param>
223 /// <returns></returns>
224 public static byte[] StrToByteArray(string str, Encoding enc)
225 {
226 return enc.GetBytes(str);
227 }
228
229 /// <summary>
230 /// Byte 陣列轉成字串。
231 /// </summary>
232 /// <param name="bytes"></param>
233 /// <returns></returns>
234 public static string ByteArrayToStr(byte[] bytes)
235 {
236 System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
237 return enc.GetString(bytes);
238 }
239
240 /// <summary>
241 /// 大小寫無關的置換函式。
242 /// </summary>
243 /// <param name="input">輸入字串。</param>
244 /// <param name="newValue">要被置換的字串。</param>
245 /// <param name="oldValue">置換的新字串。</param>
246 /// <returns>A string</returns>
247 public static string CaseInsenstiveReplace(string input,
248 string oldValue, string newValue)
249 {
250 Regex regEx = new Regex(oldValue,
251 RegexOptions.IgnoreCase | RegexOptions.Multiline);
252 return regEx.Replace(input, newValue);
253 }
254
255 /// <summary>
256 /// 檢查傳入的字串是否有出現任何一個指定的字串。字串比對時有分大小寫。
257 /// </summary>
258 /// <param name="input">欲檢查的字串</param>
259 /// <param name="hasWords">要搜尋的字詞。</param>
260 /// <returns>有符合的字詞</returns>
261 public static MatchCollection HasWords(string input,
262 params string[] hasWords)
263 {
264 StringBuilder sb = new StringBuilder(hasWords.Length + 50);
265 //sb.Append("[");
266
267 foreach (string s in hasWords)
268 {
269 sb.AppendFormat("({0})|",
270 HttpUtility.HtmlEncode(s.Trim()));
271 }
272
273 string pattern = sb.ToString();
274 pattern = pattern.TrimEnd('|'); // +"]";
275
276 Regex regEx = new Regex(pattern, RegexOptions.Multiline);
277 return regEx.Matches(input);
278 }
279
280 /// <summary>
281 /// 將字串以MD5 編碼。
282 /// </summary>
283 /// <param name="input">欲編碼的字串。</param>
284 /// <returns>編碼過的字串</returns>
285 public static string MD5Encode(string input)
286 {
287 // Create a new instance of the
288 // MD5CryptoServiceProvider object.
289 MD5 md5Hasher = MD5.Create();
290
291 // Convert the input string to a byte
292 // array and compute the hash.
293 byte[] data=http://anonymouse.org/cgi-bin/anon-www.cgi/http://huanlin.dyndns.org/cs/blogs/huan-lins_blog/archive/2006/10/20/md5Hasher.ComputeHash(
294 Encoding.Default.GetBytes(input));
295
296 // Create a new Stringbuilder to collect the bytes
297 // and create a string.
298 StringBuilder sBuilder = new StringBuilder();
299
300 // Loop through each byte of the hashed data
301 // and format each one as a hexadecimal string.
302 for (int i = 0; i < data.Length; i++)
303 {
304 sBuilder.Append(data.ToString("x2"));
305 }
306
307 // Return the hexadecimal string.
308 return sBuilder.ToString();
309 }
310
311 /// <summary>
312 /// 刪除空白以及多餘的結束字元('\0')。
313 /// 某些WinAPI 傳回的字元陣列轉成字串之後,後面會附加結束字元和垃圾資料,便可使用此函式。
314 /// </summary>
315 /// <param name="input">輸入字串</param>
316 /// <returns>輸出字串。</returns>
317 public static string Trim(string s)
318 {
319 s = s.Trim();
320 int i = s.IndexOf('\0');
321 if (i >= 0)
322 {
323 return s.Substring(0, i);
324 }
325 return s;
326 }
327
328 /// <summary>
329 /// 驗證一個字串是否經過MD5 編碼後會與傳入的MD5 字串相符。可用來驗證使用者密碼。
330 /// </summary>
331 /// <param name="input">欲比較的字串。</param>
332 /// <param name="hash">MD5 編碼過的字串。</param>
333 /// <returns>若相符則傳回True,否則傳回False。</returns>
334 public static bool MD5Verify(string input, string md5str)
335 {
336 // Hash the input.
337 string hashOfInput = StrHelper.MD5Encode(input);
338
339 // Create a StringComparer an comare the hashes.
340 StringComparer comparer = StringComparer.OrdinalIgnoreCase;
341
342 if (0 == comparer.Compare(hashOfInput, md5str))
343 {
344 return true;
345 }
346 else
347 {
348 return false;
349 }
350 }
351
352 /// <summary>
353 /// 將字串中的所有全形空白轉成半形空白。
354 /// </summary>
355 /// <param name="input">輸入字串。</param>
356 /// <returns>輸出字串。</returns>
357 public static string FullShapeSpaceToSpace(string input)
358 {
359 string replace = " ";
360 string pattern = @"[ ]";
361 Regex regEx = new Regex(pattern, RegexOptions.Multiline);
362 return regEx.Replace(input, replace);
363 }
364
365 /// <summary>
366 /// 把所有空白字元轉換成HTML 空白。
367 /// </summary>
368 /// <param name="input">輸入字串。</param>
369 /// <returns>輸出字串。</returns>
370 public static string SpaceToNbsp(string input)
371 {
372 string space = " ";
373 return input.Replace(" ", space);
374 }
375
376 /// <summary>
377 /// 把所有換行符號(\n) 和(\r) 轉成HTML 斷行標籤。
378 /// </summary>
379 /// <param name="input">輸入字串。</param>
380 /// <returns>輸出字串。</returns>
381 public static string NewLineToBreak(string input)
382 {
383 Regex regEx = new Regex(@"[\n|\r]+");
384 return regEx.Replace(input, "<br />");
385 }
386
387 /// <summary>
388 /// 判斷傳入的字串是否為空字串。
389 /// 注意:空白、全形空白、Tab 字元、和換行符號均視為「空」字元。
390 /// </summary>
391 /// <param name="input"></param>
392 /// <returns></returns>
393 public static bool IsEmpty(string input)
394 {
395 string s = StrHelper.RemoveAny(input, new char[] { ' ', ' ', '\t', '\r', '\n', '\0' });
396 if (String.IsNullOrEmpty(s))
397 return true;
398 return false;
399 }
400
401 /// <summary>
402 /// 把內含16 進位值的字串轉成byte。
403 /// </summary>
404 /// <param name="input">內含16 進位值的字串,例如:1F、AE。</param>
405 /// <returns>byte 值。</returns>
406 public static byte HexStrToByte(string s)
407 {
408 return byte.Parse(s, System.Globalization.NumberStyles.HexNumber);
409 }
410
411 /// <summary>
412 /// 字串反轉。
413 /// </summary>
414 /// <param name="s">輸入字串。</param>
415 /// <returns>反轉後的字串。</returns>
416 public static string Reverse(string s)
417 {
418 if (String.IsNullOrEmpty(s))
419 return "";
420 char[] charArray = new char[s.Length];
421 int len = s.Length - 1;
422 for (int i = 0; i <= len; i++) {
423 charArray = s[len-i];
424 }
425 return new string(charArray);
426 }
427 }
428 }
Filed under: .NET General
Attachment(s): CopySourceAsHtml_v2_UncodeFix.zip
評論
# re: 好用的 CopySourceAsHtml,我的中文修正版
2006年10月23日 上午 10:45 by Davis
真沒想到在最不可能處找到這軟體的修正,真是感謝^^
# re: 好用的 CopySourceAsHtml,我的中文修正版
2006年10月23日 上午 10:52 by huanlin
You are welcome :)
but.....最不可能處 ? ^_^
# re: 好用的 CopySourceAsHtml,我的中文修正版
2006年10月24日 上午 02:43 by hikari
剛試了一下..發現使用VS2005中文版的話..要改一下Source Code.^^
主要是修改選單文字為中文標籤文字.
Connect.cs
<!--
{\rtf1\ansi\ansicpg\lang1024\noproof950\uc1 \deff0{\fonttbl{\f0\fnil\fcharset136\fprq1 細明體;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0??;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;??\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;??\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;??\red192\green192\blue192;}??\fs18 commandBars = (\cf10 CommandBars\cf0 )\cf2 this\cf0 .application.CommandBars;\par ?? editPopup = (\cf10 CommandBarPopup\cf0 )commandBars[\cf13 "MenuBar"\cf0 ].Controls[\cf13 "\uc2\u32232 \'bds\u-28881 \'bf\'e8\uc1(&E)"\cf0 ];\par ??\par ?? copyIndex = FindCopyOnCommandBar(\cf13 "\uc2\u32232 \'bds\u-28881 \'bf\'e8\uc1(&E)"\cf0 ) + 1;}
-->
<div style="font-family: 細明體; font-size: 9pt; color: black; background: white;">
<p style="margin: 0px;"><span style="color: #2b91af;"> 194</span> commandBars = (<span style="color: teal;">CommandBars</span>)<span style="color: blue;">this</span>.application.CommandBars;</p>
<p style="margin: 0px;"><span style="color: #2b91af;"> 195</span> editPopup = (<span style="color: teal;">CommandBarPopup</span>)commandBars[<span style="color: maroon;">"MenuBar"</span>].Controls[<span style="color: maroon;">"編輯(&E)"</span>];</p>
<p style="margin: 0px;"><span style="color: #2b91af;"> 196</span> </p>
<p style="margin: 0px;"><span style="color: #2b91af;"> 197</span> copyIndex = FindCopyOnCommandBar(<span style="color: maroon;">"編輯(&E)"</span>) + 1;</p>
</div>
# re: 好用的 CopySourceAsHtml,我的中文修正版
2006年10月24日 下午 02:10 by huanlin
To Hikari:
我已經將您提供的程式碼更新到這篇文章了。
感謝!!
# re: 好用的 CopySourceAsHtml,我的中文修正版
2006年11月1日 上午 11:38 by Davis
想來是小弟用錯詞語^^
其實只是想說,小弟在很多地方遍尋不著,卻意外在此地發現。
是真的蠻意外的^^
# re: 好用的 CopySourceAsHtml,我的中文修正版
2006年11月1日 上午 11:45 by huanlin
To Davis:
瞭解,Don't mind. ^_^
該隨筆已禁止評論
from http://huanlin.dyndns.org/cs/blogs/huan-lins_blog/archive/2006/10/20/CopySourceAsHtml-Fix.aspx
1 条评论:
這篇文章的出處是:
http://huanlin.dyndns.org/cs/
格主應該一開始就標明,且不應該連網友的回覆也一並複製上來。
发表评论