测试字符串是否为guid而不抛出异常?

时间:2008-09-19 19:35:34

标签: c# string parsing guid

我想尝试将字符串转换为Guid,但我不想依赖捕获异常(

  • 出于性能原因 - 例外情况很昂贵
  • 出于可用性原因 - 调试器弹出
  • 出于设计原因 - 预期并非例外

换句话说代码:

public static Boolean TryStrToGuid(String s, out Guid value)
{
    try
    {
        value = new Guid(s);
        return true;
    }
    catch (FormatException)
    {
        value = Guid.Empty;
        return false;
    }
}

不合适。

我会尝试使用RegEx,但由于guid可以括号括起来,括号包裹,没有包装,使其变得困难。

此外,我认为某些Guid值无效(?)


更新1

ChristianK有一个好主意只能抓住FormatException,而不是全部。更改了问题的代码示例以包含建议。


更新2

为什么要担心抛出异常?我真的经常期待无效的GUID吗?

答案是。这就是我使用TryStrToGuid的原因 - 我期待不良数据。

示例1 Namespace extensions can be specified by appending a GUID to a folder name。我可能正在解析文件夹名称,检查最终之后的文本是否为GUID。

c:\Program Files
c:\Program Files.old
c:\Users
c:\Users.old
c:\UserManager.{CE7F5AA5-6832-43FE-BAE1-80D14CD8F666}
c:\Windows
c:\Windows.old

示例2 我可能正在运行一个使用频繁的网络服务器,想要检查一些发布的数据的有效性。我不希望无效数据占用比需要高2-3个数量级的资源。

示例3 我可能正在解析用户输入的搜索表达式。

enter image description here

如果他们输入GUID,我想特别处理它们(例如专门搜索该对象,或在响应文本中突出显示并格式化该特定搜索词。)


更新3 - 效果基准

测试转换10,000个好Guids和10,000个坏Guids。

Catch FormatException:
   10,000 good:     63,668 ticks
   10,000 bad:   6,435,609 ticks

Regex Pre-Screen with try-catch:
   10,000 good:    637,633 ticks
   10,000 bad:     717,894 ticks

COM Interop CLSIDFromString
   10,000 good:    126,120 ticks
   10,000 bad:      23,134 ticks

P.S。我不应该为一个问题辩护。

18 个答案:

答案 0 :(得分:107)

效果基准

Catch exception:
   10,000 good:    63,668 ticks
   10,000 bad:  6,435,609 ticks

Regex Pre-Screen:
   10,000 good:   637,633 ticks
   10,000 bad:    717,894 ticks

COM Interop CLSIDFromString
   10,000 good:   126,120 ticks
   10,000 bad:     23,134 ticks

COM Intertop(最快)答案:

/// <summary>
/// Attempts to convert a string to a guid.
/// </summary>
/// <param name="s">The string to try to convert</param>
/// <param name="value">Upon return will contain the Guid</param>
/// <returns>Returns true if successful, otherwise false</returns>
public static Boolean TryStrToGuid(String s, out Guid value)
{
   //ClsidFromString returns the empty guid for null strings   
   if ((s == null) || (s == ""))   
   {      
      value = Guid.Empty;      
      return false;   
   }

   int hresult = PInvoke.ObjBase.CLSIDFromString(s, out value);
   if (hresult >= 0)
   {
      return true;
   }
   else
   {
      value = Guid.Empty;
      return false;
   }
}


namespace PInvoke
{
    class ObjBase
    {
        /// <summary>
        /// This function converts a string generated by the StringFromCLSID function back into the original class identifier.
        /// </summary>
        /// <param name="sz">String that represents the class identifier</param>
        /// <param name="clsid">On return will contain the class identifier</param>
        /// <returns>
        /// Positive or zero if class identifier was obtained successfully
        /// Negative if the call failed
        /// </returns>
        [DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = true)]
        public static extern int CLSIDFromString(string sz, out Guid clsid);
    }
}

底线:如果您需要检查字符串是否为guid,并且您关心性能,请使用COM Interop。

如果您需要将String表示中的guid转换为Guid,请使用

new Guid(someString);

答案 1 :(得分:88)

一旦.net 4.0可用,您就可以使用Guid.TryParse()

答案 2 :(得分:66)

你不会喜欢这个,但是什么让你认为捕捉异常会变慢?

与成功的算法相比,您期望解析GUID的失败尝试次数是多少?

我的建议是使用您刚刚创建的功能并分析您的代码。如果您发现此功能确实是一个热点然后修复它,而不是之前。

答案 3 :(得分:39)

在.NET 4.0中,您可以编写如下代码:

public static bool IsValidGuid(string str)
{
    Guid guid;
    return Guid.TryParse(str, out guid);
}

答案 4 :(得分:21)

我至少会将其重写为:

try
{
  value = new Guid(s);
  return true;
}
catch (FormatException)
{
  value = Guid.Empty;
  return false;
}

您不希望在SEHException,ThreadAbortException或其他致命或非相关内容中说“无效GUID”。

更新:从.NET 4.0开始,Guid有一套新的方法:

真的,应该使用那些(如果仅仅是因为它们并非在内部使用try-catch实现“天真地”)。

答案 5 :(得分:13)

Interop比捕获异常更慢:

在快乐的道路上,拥有10,000个Guids:

Exception:    26ms
Interop:   1,201ms

在不愉快的道路上:

Exception: 1,150ms
  Interop: 1,201ms

它更加一致,但它也一直较慢。在我看来,配置调试器最好只打破未处理的异常。

答案 6 :(得分:9)

嗯,这是你需要的正则表达式......

^[A-Fa-f0-9]{32}$|^({|\\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\\))?$|^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$

但这仅适用于初学者。您还必须验证日期/时间等各个部分是否在可接受的范围内。我无法想象这比你已经概述的try / catch方法更快。希望您没有收到那么多无效的GUID来保证这种类型的支票!

答案 7 :(得分:5)

  

出于可用性原因 - 弹出调试器

如果您要使用try / catch方法,可以添加[System.Diagnostics.DebuggerHidden]属性,以确保调试器不会中断,即使您已将其设置为中断时抛出。

答案 8 :(得分:4)

虽然 是真的,但是使用错误会更加昂贵,大多数人认为他们的大部分GUID都是计算机生成的,因此TRY-CATCH并不太昂贵,因为它仅在CATCH生成费用。您可以通过对two(用户公开,无密码)的简单测试来证明这一点。

你走了:

using System.Text.RegularExpressions;


 /// <summary>
  /// Validate that a string is a valid GUID
  /// </summary>
  /// <param name="GUIDCheck"></param>
  /// <returns></returns>
  private bool IsValidGUID(string GUIDCheck)
  {
   if (!string.IsNullOrEmpty(GUIDCheck))
   {
    return new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$").IsMatch(GUIDCheck);
   }
   return false;
  }

答案 9 :(得分:4)

我有类似的情况,我注意到几乎从来没有36个字符长的无效字符串。所以基于这个事实,我改变了你的代码以获得更好的性能,同时仍然保持简单。

public static Boolean TryStrToGuid(String s, out Guid value)
{

     // this is before the overhead of setting up the try/catch block.
     if(value == null || value.Length != 36)
     {  
        value = Guid.Empty;
        return false;
     }

    try
    {
        value = new Guid(s);
        return true;
    }
    catch (FormatException)
    {
        value = Guid.Empty;
        return false;
    }
}

答案 10 :(得分:2)

据我所知,mscrolib中没有类似Guid.TryParse的东西。根据Reference Source,Guid类型具有超级复杂的构造函数,它检查各种guid格式并尝试解析它们。即使通过反射,也没有可以调用的辅助方法。我认为你必须搜索第三方Guid解析器,或编写自己的解析器。

答案 11 :(得分:2)

通过RegEx或一些执行完整性检查的自定义代码运行潜在的GUID,以确保strig至少看起来像GUID并且只包含有效字符(并且可能看起来符合整体格式)。如果没有通过完整性检查,则返回错误 - 这可能会清除绝大多数无效字符串。

然后转换上面的字符串,仍然捕获通过完整性检查的少数无效字符串的异常。

Jon Skeet对解析Ints(在TryParse在Framework中之前)做了类似的分析:Checking if a string can be converted to Int32

然而,正如AnthonyWJones所示,你可能不应该担心这一点。

答案 12 :(得分:1)

 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }

答案 13 :(得分:1)

  • 获取反射器
  • copy'n'paste Guid's .ctor(String)
  • 用“return false”替换每次出现的“throw new ...”。

Guid的ctor几乎是一个编译的正则表达式,这样你就可以获得完全相同的行为,而不会产生异常开销。

  1. 这是否构成逆向工程?我认为确实如此,因此可能是非法的。
  2. 如果GUID表单发生更改,将会中断。
  3. 更酷的解决方案是通过动态替换“抛出新”来动态检测方法。

答案 14 :(得分:1)

我投票通过上面Jon或类似解决方案(IsProbablyGuid)发布的GuidTryParse链接。我将为我的转换库编写一个。

我认为这个问题必须如此复杂才是完全蹩脚的。如果Guid可能为空,那么“是”或“as”关键字就可以了。但出于某种原因,即使SQL Server没问题,.NET也不行。为什么? Guid.Empty的价值是多少?这只是.NET设计中产生的一个愚蠢的问题,当语言的约定逐步实现时,它真的让我感到困惑。到目前为止,性能最佳的答案是使用COM Interop,因为Framework没有优雅地处理它? “这个字符串可以是GUID吗?”应该是一个容易回答的问题。

依赖被抛出的异常是可以的,直到应用程序上网。那时我只是为拒绝服务攻击做好准备。即使我没有受到“攻击”,我知道有些雅虎会使用URL,或者我的营销部门会发出一个格式错误的链接,然后我的应用程序必须遭受相当大的性能打击,可能会带来在服务器上,因为我没有编写我的代码来处理不应该发生的问题,但我们都知道会发生这种情况。

这在“异常”上稍微模糊了一下 - 但是底线,即使问题很少发生,如果它在很短的时间内发生了足够的时间,你的应用程序崩溃了对它的捕获服务,那么我认为扔一个例外是不好的形式。

TheRage3K

答案 15 :(得分:0)

使用C#中的扩展方法

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}

答案 16 :(得分:0)

Private Function IsGuidWithOptionalBraces(ByRef strValue As String) As Boolean
    If String.IsNullOrEmpty(strValue) Then
        Return False
    End If

    Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^[\{]?[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}[\}]?$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
End Function


Private Function IsGuidWithoutBraces(ByRef strValue As String) As Boolean
    If String.IsNullOrEmpty(strValue) Then
        Return False
    End If

    Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
End Function


Private Function IsGuidWithBraces(ByRef strValue As String) As Boolean
    If String.IsNullOrEmpty(strValue) Then
        Return False
    End If

    Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
End Function

答案 17 :(得分:0)

如果TypeOf ctype(myvar,Object)是Guid那么.....