以前為了趕項目遇到這種需求時,也沒過多考慮效能因素,隨便寫了一個(現在看起來很原始的)方法來實現:
static bool IsGuidByError(string strSrc) { if (String.IsNullOrEmpty(strSrc)) { return false; } bool _result = false; try { Guid _t = new Guid(strSrc); _result = true; } catch { } return _result; }
但是大家都知道,異常捕獲是要有效能損耗的。今天想了想:其實還有其它方法,也許會更好
static bool IsGuidByReg(string strSrc) { Regex reg = new Regex("^[A-F0-9]{8}(-[A-F0-9]{4}){3}-[A-F0-9]{12}$", RegexOptions.Compiled); return reg.IsMatch(strSrc); }
此外,根據Guid的格式規則:總長數36位,由4個'-'分隔,每一段都是由數字+英文字母組合而成。也可以自己寫一個演算法:
static bool IsGuidByArr(string strSrc) { if (String.IsNullOrEmpty(strSrc) || strSrc.Length!=36) { return false; } string[] arr = strSrc.Split('-'); if (arr.Length != 5) { return false; } for (int i = 0; i < arr.Length; i++) { for (int j = 0; j < arr[i].Length; j++) { char a = arr[i][j]; if (!((a >= 48 && a <= 57) || (a >= 65 && a <= 90) || (a >= 97 && a <= 122))) { return false; } } } return true; }
另:經獵風同學提醒,應該還要考慮到Regex不編譯的情況,所以再加上這一段
static bool IsGuidByRegNoComplied(string strSrc) { Regex reg = new Regex("^[A-F0-9]{8}(-[A-F0-9]{4}){3}-[A-F0-9]{12}$"); return reg.IsMatch(strSrc); }
此外:塵塵同學在回複中提醒到還有Guid的TryParse/Parse方法(不過該方法是.Net 4.0才新增加的)
static bool IsGuidByParse(string strSrc) { Guid g = Guid.Empty; return Guid.TryParse(strSrc, out g); }
對於獵風與塵塵的反饋表示感謝!ok,搞了這麼多方法,是騾子是馬,溜溜便知:
先測試字串格式正常的情況
using System;using System.Diagnostics;using System.Text.RegularExpressions;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { string a = "C0869370-70BF-4408-A8CF-72A77BB1D788"; Console.WriteLine(IsGuidByError(a)); Console.WriteLine(IsGuidByReg(a)); Console.WriteLine(IsGuidByRegNoComplied(a)); Console.WriteLine(IsGuidByArr(a)); Console.WriteLine(IsGuidByParse(a)); Console.WriteLine("測試開始------------------->\n"); Stopwatch sw = new Stopwatch(); int count = 5000; int times = 5; long result = 0; for (int i = 0; i < times; i++) { result += Test(sw, count, a, MethodType.異常); } Console.WriteLine("\n{0}次×{1}輪測試,[異常]方法平均每輪速度:{2}\n", count, times, result / times); Console.Write("\n"); result = 0; for (int i = 0; i < times; i++) { result += Test(sw, count, a, MethodType.正則); } Console.WriteLine("\n{0}次×{1}輪測試,[正則]方法平均每輪速度:{2}\n", count, times, result / times); Console.Write("\n"); result = 0; for (int i = 0; i < times; i++) { result += Test(sw, count, a, MethodType.正則不編譯); } Console.WriteLine("\n{0}次×{1}輪測試,[正則不編譯]方法平均每輪速度:{2}\n", count, times, result / times); Console.Write("\n"); result = 0; for (int i = 0; i < times; i++) { result += Test(sw, count, a, MethodType.數組); } Console.WriteLine("\n{0}次×{1}輪測試,[數組]方法平均每輪速度:{2}\n", count, times, result / times); result = 0; for (int i = 0; i < times; i++) { result += Test(sw, count, a, MethodType.TryParse); } Console.WriteLine("\n{0}次×{1}輪測試,[TryParse]方法平均每輪速度:{2}\n", count, times, result / times); Console.Read(); } static bool IsGuidByArr(string strSrc) { if (String.IsNullOrEmpty(strSrc) || strSrc.Length!=36) { return false; } string[] arr = strSrc.Split('-'); if (arr.Length != 5) { return false; } for (int i = 0; i < arr.Length; i++) { for (int j = 0; j < arr[i].Length; j++) { char a = arr[i][j]; if (!((a >= 48 && a <= 57) || (a >= 65 && a <= 90) || (a >= 97 && a <= 122))) { return false; } } } return true; } static bool IsGuidByError(string strSrc) { if (String.IsNullOrEmpty(strSrc)) { return false; } bool _result = false; try { Guid _t = new Guid(strSrc); _result = true; } catch { } return _result; } static bool IsGuidByReg(string strSrc) { Regex reg = new Regex("^[A-F0-9]{8}(-[A-F0-9]{4}){3}-[A-F0-9]{12}$", RegexOptions.Compiled); return reg.IsMatch(strSrc); } static bool IsGuidByRegNoComplied(string strSrc) { Regex reg = new Regex("^[A-F0-9]{8}(-[A-F0-9]{4}){3}-[A-F0-9]{12}$"); return reg.IsMatch(strSrc); } static bool IsGuidByParse(string strSrc) { Guid g = Guid.Empty; return Guid.TryParse(strSrc, out g); } /// <summary> /// 測試 /// </summary> /// <param name="sw"></param> /// <param name="count"></param> /// <param name="a"></param> /// <param name="useRegularExpressions"></param> /// <returns></returns> static long Test(Stopwatch sw, int count, string a, MethodType type) { bool _test = false; int i = 0; sw.Reset(); sw.Start(); for (i = 0; i < count; i++) { switch (type) { case MethodType.異常: _test = IsGuidByError(a); break; case MethodType.數組: _test = IsGuidByArr(a); break; case MethodType.正則: _test = IsGuidByReg(a); break; case MethodType.正則不編譯: _test = IsGuidByReg(a); break; case MethodType.TryParse: _test = IsGuidByParse(a); break; default: break; } } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "\n"); return sw.ElapsedMilliseconds; } enum MethodType { 異常, 數組, 正則, 正則不編譯, TryParse } }}
True
True
True
True
True
測試開始------------------->
5
5
5
5
5
5000次×5輪測試,[異常]方法平均每輪速度:5
9219
9235
9360
9272
9103
5000次×5輪測試,[正則]方法平均每輪速度:9237
9095
9113
9116
9181
9156
5000次×5輪測試,[正則不編譯]方法平均每輪速度:9132
9
5
7
5
6
5000次×5輪測試,[數組]方法平均每輪速度:6
4
4
4
4
4
5000次×5輪測試,[TryParse]方法平均每輪速度:4
可以看到,在字串格式正確的情況下,異常未被觸發,除Regex顯得巨慢以外,其它三種方法相差無已。
再來看下字串格式錯誤的情況下
把string a = "C0869370-70BF-4408-A8CF-72A77BB1D788";改成string a = "C0869370-70BF-4408-A8CF-72A77BB1D788111111111111";
輸出結果如下:
False
False
False
False
False
測試開始------------------->
35575
33681
33752
33985
33804
5000次×5輪測試,[異常]方法平均每輪速度:34159
8982
9104
9087
8959
8973
5000次×5輪測試,[正則]方法平均每輪速度:9021
9041
9102
9056
8977
8872
5000次×5輪測試,[正則不編譯]方法平均每輪速度:9009
0
0
0
0
0
5000次×5輪測試,[數組]方法平均每輪速度:0
1
1
1
1
1
5000次×5輪測試,[TryParse]方法平均每輪速度:1
很明顯,這時候異常帶來的效能開銷就很可觀了,反而基於“字元數組”的檢測方法最快(這跟測試案例有關,因為該字串長度大於36,直接就出局了,連後面的迴圈都不用,如果換成其它錯誤的格式比如:“C0869370-70BF-4408-A8CF-72A77BB1D78?”,可能略有差異)
結論:綜合考慮,推薦大家用“基於字元數組”的檢測方法或Guid內建的TryParse方法,異常捕獲和Regex方法應該避免使用。