以前为了赶项目遇到这种需求时,也没过多考虑性能因素,随便写了一个(现在看起来很原始的)方法来实现:
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; }
另:经猎风同学提醒,应该还要考虑到正则表达式不编译的情况,所以再加上这一段
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
可以看到,在字符串格式正确的情况下,异常未被触发,除正则表达式显得巨慢以外,其它三种方法相差无已。
再来看下字符串格式错误的情况下
把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方法,异常捕获和正则表达式方法应该避免使用。