在博客園開了博客,也算是把自己的東西分享一下,希望自己能堅持下去吧。
第一篇其實是個小東西,就是字符串的 Unicode 轉義。有時候在處理一些網絡信息的時候,難免要對中文進行 Unicode 轉義,就是變成 \u4E2D\u6587 的樣子,以便於進行網絡傳輸。為了方便編碼和解碼,就寫了兩個函數來做這件事。
首先是十六進制字符的判斷,我把它放到了單獨的類中以方便重用。實現的功能很簡單,就是判斷字符是否在 [0-9A-Fa-f] 的范圍內。這里有一個很小的技巧,就是按照二叉排序樹的順序把判斷的順序稍微微調一下,能夠略微提高一些速度。
using System.Text; namespace Cyjb { /// <summary> /// 提供 <see cref="System.Char"/> 類的擴展方法。 /// </summary> public static class CharExt { #region IsHex /// <summary> /// 指示指定的 Unicode 字符是否屬於十六進制數字類別。 /// </summary> /// <param name="ch">要計算的 Unicode 字符。</param> /// <returns>如果 <paramref name="ch"/> 是十進制數字,則為 <c>true</c>; /// 否則,為 <c>false</c>。</returns> public static bool IsHex(this char ch) { if (ch <= 'f') { if (ch >= 'A') { return ch <= 'F' || ch >= 'a'; } else { return ch >= '0' && ch <= '9'; } } return false; } /// <summary> /// 指示指定字符串中位於指定位置處的字符是否屬於十六進制數字類別。 /// </summary> /// <param name="str">一個字符串。</param> /// <param name="index">要計算的字符在 <paramref name="str"/> 中的位置。</param> /// <returns>如果 <paramref name="str"/> 中位於 <paramref name="index"/> 處的字符是十進制數字, /// 則為 <c>true</c>;否則,為 <c>false</c>。</returns> /// <exception cref="System.IndexOutOfRangeException"><paramref name="index"/> 大於等於字符串的長度或小於零。</exception> public static bool IsHex(string str, int index) { return IsHex(str[index]); } #endregion // IsHex } }
然后就是對字符串進行 Unicode 編碼和解碼的方法了。
解碼方法支持 \x,\u 和 \U 轉義,其中 \x 之后可跟 1~4 個十六進制字符,\u 后面是 4 個十六進制字符,\U 后面則是 8 個,而且由於 .Net 只支持小於 0x10FFFF 的 Unicode,所以使用 \U 轉義時,超出的部分會被舍棄。如果不滿足上面的情況,則不會進行轉義,也不會報錯。
編碼會將可顯示字符(0x20~0x7E,就是從空格到~)以外的所有字符使用 \u 轉義表示。
using System.Text; namespace Cyjb { /// <summary> /// 提供 <see cref="System.String"/> 類的擴展方法。 /// </summary> public static class StringExt { #region Unicode 操作 /// <summary> /// 將字符串中的 \u,\U 和 \x 轉義轉換為對應的字符。 /// </summary> /// <param name="str">要轉換的字符串。</param> /// <returns>轉換后的字符串。</returns> public static string DecodeUnicode(this string str) { if (string.IsNullOrEmpty(str)) { return str; } int idx = str.IndexOf('\\'); if (idx < 0) { return str; } int len = str.Length, start = 0; StringBuilder builder = new StringBuilder(len); while (idx >= 0) { // 添加當前 '\' 之前的字符串。 if (idx > start) { builder.Append(str, start, idx - start); start = idx; } // 跳過 '\' 字符。 idx++; // '\' 字符后的字符數小於 2,不可能是轉義字符,直接返回。 if (idx + 1 >= len) { break; } // 十六進制字符的長度。 int hexLen = 0; // 處理 Unicode 轉義。 switch (str[idx]) { case 'x': // \x 后面可以是 1 至 4 位。 hexLen = GetHexLength(str, idx + 1, 4); break; case 'u': // \u 后面必須是 4 位。 if (idx + 4 < len && GetHexLength(str, idx + 1, 4) == 4) { hexLen = 4; } else { hexLen = 0; } break; case 'U': // \U 后面必須是 8 位。 if (idx + 8 < len && GetHexLength(str, idx + 1, 8) == 8) { hexLen = 8; } else { hexLen = 0; } break; } if (hexLen > 0) { idx++; int charNum = int.Parse(str.Substring(idx, hexLen), NumberStyles.HexNumber, CultureInfo.InvariantCulture); if (charNum < 0xFFFF) { // 單個字符。 builder.Append((char)charNum); } else { // 代理項對的字符。 builder.Append(char.ConvertFromUtf32(charNum & 0x1FFFFF)); } idx = start = idx + hexLen; } idx = str.IndexOf('\\', idx); } // 添加剩余的字符串。 if (start < len) { builder.Append(str.Substring(start)); } return builder.ToString(); } /// <summary> /// 返回字符串指定索引位置之后的十六進制字符的個數。 /// </summary> /// <param name="str">要獲取十六進制字符個數的字符串。</param> /// <param name="index">要開始計算十六進制字符個數的其實索引。</param> /// <param name="maxLength">需要的最長的十六進制字符個數。</param> /// <returns>實際的十六進制字符的個數。</returns> internal static int GetHexLength(string str, int index, int maxLength) { if (index + maxLength > str.Length) { maxLength = str.Length - index; } for (int i = 0; i < maxLength; i++, index++) { if (!CharExt.IsHex(str, index)) { return i; } } return maxLength; } /// <summary> /// 將字符串中不可顯示字符(0x00~0x1F,0x7F之后)轉義為 \\u 形式,其中十六進制以大寫字母形式輸出。 /// </summary> /// <param name="str">要轉換的字符串。</param> /// <returns>轉換后的字符串。</returns> public static string EncodeUnicode(this string str) { return EncodeUnicode(str, true); } /// <summary> /// 將字符串中不可顯示字符(0x00~0x1F,0x7F之后)轉義為 \\u 形式。 /// </summary> /// <param name="str">要轉換的字符串。</param> /// <param name="upperCase">是否以大寫字母形式輸出十六進制。如果為 <c>true</c> 則是以大寫字母形式輸出十六進制, /// 否則以小寫字母形式輸出。</param> /// <returns>轉換后的字符串。</returns> public static string EncodeUnicode(this string str, bool upperCase) { if (string.IsNullOrEmpty(str)) { return str; } string format = upperCase ? "X4" : "x4"; StringBuilder builder = new StringBuilder(str.Length * 2); for (int i = 0; i < str.Length; i++) { char c = str[i]; if (c >= ' ' && c <= '~') { // 可顯示字符。 builder.Append(c); } else { builder.Append("\\u"); builder.Append(((int)c).ToString(format, CultureInfo.InvariantCulture)); } } return builder.ToString(); } #endregion } }
代碼可見 https://github.com/CYJB/Cyjb/tree/master/Cyjb 這里的 CharExt 和 StringExt 類。