예외가 발생하지 않고 string이 guid인지 테스트합니까?


180

문자열을 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

ChristianKFormatException모두가 아니라 잡는 것이 좋습니다 . 제안을 포함하도록 질문의 코드 샘플을 변경했습니다.


업데이트 2

예외 발생에 대해 왜 걱정해야합니까? 나는 종종 잘못된 GUID를 기대하고 있습니까?

대답은 ' 예' 입니다. 나는 - 나는 TryStrToGuid을 사용하고 이유입니다 하고 잘못된 데이터를 기대.

예 1 폴더 이름에 GUID를 추가하여 네임 스페이스 확장명을 지정할 수 있습니다 . 폴더 이름을 파싱하여 final 다음에 텍스트가 있는지 확인합니다 . 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 사용자가 입력 한 검색 표현식을 구문 분석하고있을 수 있습니다.

여기에 이미지 설명을 입력하십시오

그들이 GUID에 들어가면 특별히 처리하고 싶습니다 (예 : 해당 객체 검색 또는 응답 텍스트에서 특정 검색어를 강조 표시하고 형식 지정).


업데이트 3-성능 벤치 마크

10,000 개의 좋은 Guid와 10,000 개의 나쁜 Guid를 변환하는 테스트.

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

추신 : 나는 질문을 정당화 할 필요가 없습니다.


7
왜 세상에서 이것이 커뮤니티 위키입니까?
Jeff

36
네가 옳아; 질문을 정당화 할 필요는 없습니다 . 그러나 나는 관심을 가지고 정당성을 읽습니다 (왜 내가 이것을 읽는지와 매우 유사하기 때문에). 칭찬 해 주셔서 감사합니다.
bw

2
@Jeff는 OP가 10 회 이상 편집 했으므로 아마 커뮤니티 위키에서 메타
Marijn

3
Guid.TryParse 또는 Guid.TryParseExact 솔루션에 대해서는이 페이지를 계속 살펴보십시오. .NET 4.0 + 위의 솔루션은 가장 우아한 아니다
dplante

1
@dplante 내가 2008 년에 처음 질문했을 때는 없었습니다 4.0. 그렇기 때문에 질문과 받아 들여진 대답이 바로 그런 이유입니다.
Ian Boyd

답변:


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);

8
디버거를 켜거나 끈 상태에서 실행 했습니까? 예외 처리 성능은 디버거를 연결하지 않고 몇 배로 향상됩니다.
Daniel T.

감사합니다. 나는이 질문을 직접하려고했다. 당신의 답을 찾았습니다.
David

위에서 네임 스페이스 PInvoke 코드 스 니펫을 사용하여 PInvoke.cs라는 새 파일을 작성했지만 코드를 작동시킬 수 없습니다. 디버깅 할 때 CLSIDFromString의 결과가 항상 음수임을 알 수 있습니다. 호출 줄을 다음과 같이 변경하려고 시도했습니다. int hresult = PInvoke.ObjBase.CLSIDFromString (Guid.NewGuid (). ToString (), out value); 그러나 여전히 부정적입니다. 내가 뭘 잘못하고 있죠?
JALLRED

88

.net 4.0이 사용 가능하면을 사용할 수 있습니다 Guid.TryParse().


8
더 빠른 방법 중 하나는 Guid.TryParseExact () 메서드를 사용하는 것입니다.

4
Guid string을 구문 분석하는 것이 응용 프로그램의 가장 느린 부분이라면 축복을받습니다.
환불 불가 환불 불가

65

당신은 이것을 좋아하지 않을 것입니다. 그러나 예외를 잡는 것이 느리다고 생각하게 만드는 것은 무엇입니까?

성공적인 GUID와 비교할 때 GUID 구문 분석에 실패한 횟수는 몇 번입니까?

내 조언은 방금 만든 기능을 사용하고 코드를 프로파일 링하는 것입니다. 이 기능은 진정으로 핫스팟 것을 발견 할 경우 다음 하지만 전에 그것을 해결.


2
좋은 대답, 조기 최적화는 모든 악의 근원입니다.
Kev

33
예외가 아닌 예외에 의존하는 것은 좋지 않습니다. 다른 사람이 들어가기를 원하지 않는 나쁜 습관입니다. 그리고 특히 사람들이 그것이 잘 작동한다고 믿고 라이브러리 루틴에서 그것을하고 싶지 않습니다.
Ian Boyd

익명의 원래 질문은 예외를 피하려는 이유로 성능을 언급했습니다. 그렇지 않다면 아마도 질문을 조정해야합니다.
AnthonyWJones

6
예외적 인 경우에는 예외를 사용해야합니다. '의미 : 개발자가 관리하지 않습니다. 저는 오류를 관리하는 Microsoft의 '모든 예외'방식에 반대합니다. 방어적인 프로그래밍 규칙. Microsoft 프레임 워크 개발자에게 Guid 클래스에 'TryParse'를 추가하십시오.
Mose

14
내 자신의 의견에 응답 => Guid.TryParse 프레임 워크 4.0에 추가되었습니다 --- msdn.microsoft.com/en-us/library/... --- 같은 빠른 반응 thxs의 MS)
모스

39

.NET 4.0에서는 다음과 같이 작성할 수 있습니다.

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

3
이것은 실제로 최고의 답변 중 하나 여야합니다.
톰 린트

21

최소한 다음과 같이 다시 작성합니다.

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

SEHException, ThreadAbortException 또는 기타 치명적이거나 관련이없는 것들에 대해 "유효하지 않은 GUID"라고 말하고 싶지 않습니다.

업데이트 : .NET 4.0부터 Guid에 사용할 수있는 새로운 메소드가 있습니다.

실제로, 그것들은 try-catch를 사용하여 내부적으로 "순진하게"구현되지 않았다는 사실 만 사용해야합니다.


13

Interop은 예외를 잡는 것보다 느립니다.

10,000 명의 길드가있는 행복한 길에서

Exception:    26ms
Interop:   1,201ms

불행한 길에서 :

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

더 일관되지만 느리게 느립니다. 처리되지 않은 예외 만 위반하도록 디버거를 구성하는 것이 더 나을 것 같습니다.


"디버거가 처리되지 않은 예외에서만 중단됩니다"옵션이 아닙니다.
Ian Boyd

1
@Ian 보이드 - 당신이 (익스프레스 포함) VS 버전 중 하나를 사용하는 경우, 그것은 입니다 옵션. msdn.microsoft.com/en-us/library/038tzxdw.aspx .
Mark Brackett

1
나는 그것이 실현 가능한 옵션이 아니라는 것을 의미했다. "실패는 옵션이 아닙니다." 그것은 이다 옵션,하지만 난 사용하지 않을 오전 하나.
Ian Boyd

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가 많지 않기를 바랍니다.


음, 타임 스탬프에서 생성 된 IIRC GUID는 일반적으로 나쁜 생각으로 간주되며 다른 종류 (유형 4)는 완전히 임의적입니다.
BCS

5

유용성으로 인해 디버거가 나타납니다.

try / catch 접근 방식을 사용하려는 경우 [System.Diagnostics.DebuggerHidden] 속성을 추가하여 디버거가 던질 때 중단하도록 설정 한 경우에도 중단되지 않도록 할 수 있습니다.


4

오류를 사용하는 것이 더 비싸다는 것은 사실 이지만 , 대부분의 사람들은 GUID의 대부분이 컴퓨터로 생성 될 것이므로 TRY-CATCH비용이 많이 들기 때문에 너무 비싸지 않다고 생각합니다 CATCH. 당신은 두 가지 의 간단한 테스트로 자신에게 이것을 증명할 수 있습니다 (사용자 공개, 비밀번호 없음) .

여기 있습니다 :

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;
  }

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;
    }
}

1
Guid는 ctor에서 대시 문자열 형식 이상의 것을 허용합니다. GUID에는 대시가있는 둘러싼 중괄호가 있거나 대시 또는 중괄호가 없을 수 있습니다. 이 코드는 대체 문자이지만 완벽하게 유효한 문자열 형식으로 사용될 때 잘못된 부정을 생성합니다.
크리스 차라 바룩

1
후속 적으로 문자열 형식 GUID의 유효한 길이는 각각 순수 16 진, 대시 및 괄호가있는 32, 36 및 38입니다.
Chris Charabaruk

1
@Chris, 당신의 요점은 유효하지만 @JBrooks는 try / catch로 배럴하기 전에 예상 GUID를 확인하는 온전한 아이디어가 특히 의심스러운 입력이 공통적 인 경우 의미가 있습니다. 아마 if (value == null || value.Length <30 || value.length> 40) {value = Guid.Empty; return false;}
bw

1
실제로, 나는 30..40보다는 32..38을 더 좁게 유지하지만 더 좋을 것입니다.
Chris Charabaruk

2

내가 아는 한 mscrolib에는 Guid.TryParse와 같은 것이 없습니다. Reference Source에 따르면 Guid 유형에는 모든 종류의 guid 형식을 확인하고 구문 분석하려는 메가 콤플렉스 생성자가 있습니다. 리플렉션을 통해서도 호출 할 수있는 도우미 메서드는 없습니다. 타사 Guid 파서를 검색하거나 직접 작성해야한다고 생각합니다.


2

스트릭이 GUID처럼 보이고 유효한 문자로만 구성되어 있는지 (그리고 전체 형식에 맞는 것처럼 보일 수 있도록) 위생 검사를 수행하는 정규식 또는 일부 사용자 정의 코드를 통해 잠재적 인 GUID를 실행하십시오. 온 전성 검사를 통과하지 못하면 오류를 반환합니다. 대부분의 유효하지 않은 문자열을 제거합니다.

그런 다음 위와 같이 문자열을 변환하여 온 전성 검사를 통과하는 몇 가지 잘못된 문자열에 대한 예외를 여전히 포착합니다.

Jon Skeet은 Int를 구문 분석하는 것과 비슷한 것을 분석했습니다 (TryParse가 프레임 워크에 있기 전에). 문자열을 Int32로 변환 할 수 있는지 확인

그러나 AnthonyWJones가 말했듯 이 아마도 이것에 대해 걱정하지 않아도됩니다.


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

"-" "{" "}"( "및") "은 ​​16 진수 문자는 유효하지 않지만 guid 문자열에서는 유효합니다.
Preston Guillot

2
이 코드를 입력 GUID 문자열이 그 이외의 진수 문자가 포함 된 경우 완벽하게 작동합니다
rupello

1
  • 반사판 가져 오기
  • Guid의 .ctor (문자열) 붙여 넣기
  • "throw new ..."의 모든 발생을 "return false"로 바꿉니다.

Guid의 ctor는 컴파일 된 정규 표현식으로 예외의 오버 헤드없이 정확히 동일한 동작을 얻을 수 있습니다.

  1. 이것이 리버스 엔지니어링을 구성합니까? 나는 그것이 그렇게 생각하는 것은 불법 일 수 있습니다.
  2. GUID 양식이 변경되면 중단됩니다.

더 멋진 솔루션은 "새로 던지기"를 즉시 대체하여 분석법을 동적으로 계측하는 것입니다.


1
ctor에서 코드를 훔치려 고했지만 많은 내부 개인 클래스를 참조하여 지원 작업을 수행합니다. 저를 믿으십시오. 그것이 나의 첫 번째 시도였습니다.
Ian Boyd

1

Jon 또는 유사한 솔루션 (IsProbablyGuid)이 게시 한 GuidTryParse 링크에 투표합니다 . 나는 나의 변환 라이브러리와 같은 것을 쓸 것이다.

이 질문이 너무 복잡해야한다는 것은 완전히 불충분하다고 생각합니다. Guid가 null 일 수있는 경우 "is"또는 "as"키워드는 괜찮습니다. 그러나 어떤 이유로 든 SQL Server가 괜찮더라도 .NET은 그렇지 않습니다. 왜? Guid.Empty의 가치는 무엇입니까? 이것은 .NET 디자인으로 인해 발생하는 바보 같은 문제이며 언어 규칙이 그 자체로 나아갈 때 실제로 버그가 있습니다. 지금까지 가장 실적이 좋은 답변은 프레임 워크가 정상적으로 처리하지 못하기 때문에 COM Interop을 사용하고 있습니까? "이 문자열은 GUID가 될 수 있습니까?" 대답하기 쉬운 질문이어야합니다.

앱이 인터넷에 연결될 때까지 예외가 발생해도 괜찮습니다. 그 시점에서 나는 서비스 거부 공격을 시작했다. "공격 당하지"않더라도 yahoo가 URL로 원숭이를 만나거나 마케팅 부서에서 잘못된 링크를 전송한다는 것을 알고 있습니다. 그러면 응용 프로그램에서 COULD가 가져 오는 상당히 많은 성능 저하가 발생합니다. SHOULDN이 발생하지 않는 문제를 처리하기 위해 코드를 작성하지 않았기 때문에 서버가 다운되었지만, 우리는 모두 일어날 것입니다.

이것은 "예외"에서 줄을 조금 흐리게합니다. 그러나 문제는 드물지만 짧은 시간 내에 충분한 시간이 발생하여 응용 프로그램이 캐치 서비스를 중단시키는 경우에 예외가 발생한다고 생각합니다. 나쁜 형태.

TheRage3K



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

0

C #에서 확장 방법으로

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.