Вопрос по string, parsing, c# – Проверить, является ли строка guid без исключения?

178

Я хочу попробовать преобразовать строку в Guid, но я не хочу полагаться на перехват исключений (

for performance reasons - exceptions are expensive for usability reasons - the debugger pops up for design reasons - the expected is not exceptional

Другими словами код:

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 являются недействительными (?)

Update 1

ChristianK была хорошая идея поймать толькоFormatException, а не все. Изменен пример кода вопроса, чтобы включить предложение.

Update 2

Зачем беспокоиться о брошенных исключениях? Неужели я так часто жду неверных идентификаторов GUID?

Ответyes, Вот почему я использую TryStrToGuid - яam ожидая плохие данные.

Example 1 Расширения пространства имен можно указать, добавив GUID к имени папки, Я мог бы анализировать имена папок, проверяя, есть ли текст после финальной. это 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

Example 2 Возможно, я использую интенсивно используемый веб-сервер, который хочет проверить достоверность некоторых опубликованных данных. Я не хочу, чтобы недействительные данные связывали ресурсы на 2-3 порядка выше, чем нужно.

Example 3 Возможно, я анализирую поисковое выражение, введенное пользователем.

enter image description here

Если они вводят GUID, я хочу обрабатывать их специально (например, специально искать этот объект или выделять и форматировать этот конкретный поисковый термин в тексте ответа).

Update 3 - Performance benchmarks

Тест на преобразование 10 000 хороших гидов и 10 000 плохих гидов.

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. I shouldn't have to justify a question.

Почему в мире это вики сообщества? Jeff
Пожалуйста, продолжайте искать на этой странице решения для Guid.TryParse или Guid.TryParseExact. С .NET 4.0 + вышеприведенное решение не самое элегантное dplante
@dplante Когда я изначально задавал вопрос в 2008 году, не было4.0, Вот почему вопрос и принятый ответ таковы. Ian Boyd
Вы правы; вам следуетnot должен обосновать вопрос. Однако я с интересом читаю обоснование (поскольку оно очень похоже на то, почему я здесь читаю это). Итак, спасибо за большое оправдание. b w
@ Джефф, вероятно, потому что ОП отредактировал его более 10 раз - см.meta on community wiki Marijn

Ваш Ответ

18   ответов
39

public static bool IsValidGuid(string str)
{
    Guid guid;
    return Guid.TryParse(str, out guid);
}
Error: User Rate Limit Exceeded
21

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

Вы не хотите сказать "неверный GUID" на SEHException, ThreadAbortException или других фатальных или не связанных вещей.

Update: Начиная с .NET 4.0, для Guid доступен новый набор методов:

Guid.TryParse Guid.TryParseExact

Действительно, их следует использовать (хотя бы потому, что они не «наивно» реализованы с использованием try-catch внутри).

Error: User Rate Limit Exceeded Ian Boyd
Guid.TryParseExcatError: User Rate Limit ExceededGuid.TryParseExact
Error: User Rate Limit Exceeded
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
88
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
1
Get Reflector copy'n'paste Guid's .ctor(String) replace every occurance of "throw new ..." with "return false".

ажение, так что вы получите точно такое же поведение без дополнительных затрат на исключение.

Does this constitute a reverse engineering? I think it does, and as such might be illegal. Will break if GUID form changes.

Еще более удачным решением было бы динамическое применение метода, заменив "throw new" на лету.

Error: User Rate Limit Exceeded Ian Boyd
1

Джон или аналогичное решение (IsProbblyGuid). Я напишу такой же для моей библиотеки конверсий.

Я думаю, что это совершенно неубедительно, что этот вопрос должен быть настолько сложным. & Quot; является & quot; или "как" ключевое слово было бы хорошо, если бы Guid мог быть нулевым. Но по какой-то причине, хотя SQL Server в порядке с этим, .NET нет. Зачем? Какова стоимость Guid.Empty? Это просто глупая проблема, созданная при разработке .NET, и она действительно беспокоит меня, когда соглашения о языке наступают сами собой. До сих пор самым эффективным ответом было использование COM Interop, потому что Framework не справляется с этим изящно? & quot; Может ли эта строка быть GUID? & quot; должен быть вопрос, на который легко ответить.

Положиться на исключение можно, пока приложение не появится в Интернете. В этот момент я просто настроился на атаку отказа в обслуживании. Даже если я не "атакован", я знаю, что некоторые Yahoo собираются поэкспериментировать с URL-адресом, или, возможно, мой отдел маркетинга отправит неверно сформированную ссылку, и тогда мое приложение должно испытать довольно высокую производительность, что МОЖЕТ остановил сервер, потому что я не написал свой код для решения проблемы, которая ДОЛЖНА случиться, но мы все знаем, что БУДЕТ ПРОИЗОЙДЕТ.

Это немного размывает строку "Exception" - но суть в том, что даже если проблема нечастая, если за короткий промежуток времени может произойти достаточное количество раз, когда ваше приложение аварийно завершает работу, обслуживая все эти ловушки, то я думаю, что исключение является плохой формой.

TheRage3K

2

хотя RegEx или некоторый пользовательский код, который проверяет работоспособность, чтобы убедиться, что стринг, по крайней мере, выглядит как GUID и состоит только из допустимых символов (и, возможно, что он соответствует общему формату). Если он не проходит проверку исправности, возвращает ошибку - это, вероятно, отсеет подавляющее большинство недопустимых строк.

Затем преобразуйте строку, как описано выше, по-прежнему перехватывая исключение для нескольких недопустимых строк, которые проходят проверку работоспособности.

Джон Скит сделал анализ для чего-то похожего для разбора Ints (до того, как TryParse был в Framework):Проверка возможности преобразования строки в Int32

Тем не менее, какAnthonyWJones указал, что вы, вероятно, не должны беспокоиться об этом.

1
 bool IsProbablyGuid(string s)
    {
        int hexchars ,= 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
5

Если вы используете подход try / catch, вы можете добавить атрибут [System.Diagnostics.DebuggerHidden], чтобы убедиться, что отладчик не сломается, даже если вы установили его как разрыв на бросок.

66

но с чего вы взяли, что поймать исключение будет медленнее?

Сколько неудачных попыток разобрать GUID вы ожидаете по сравнению с успешными?

Мой совет - используйте функцию, которую вы только что создали, и профилируйте свой код. Если вы обнаружите, что эта функция действительно горячая точкаthen исправить это, но не раньше.

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, чтобы гарантировать такой тип проверки!

Error: User Rate Limit Exceeded
107

Performance Benchmarks

   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 (Fastest) Answer:

/// <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.

Если вам нужно преобразовать guid в представлении String в Guid, используйте

new Guid(someString);
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
0

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}
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;
    }
}
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
13

На счастливом пути с 10 000 гидов:

Exception:    26ms
Interop:   1,201ms

На несчастном пути:

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

Это более согласованно, но также постоянно медленнее. Мне кажется, вам лучше настроить отладчик так, чтобы он работал только на необработанных исключениях.

Error: User Rate Limit Exceeded Ian Boyd
Error: User Rate Limit ExceededisError: User Rate Limit Exceededmsdn.microsoft.com/en-us/library/038tzxdw.aspx.
Error: User Rate Limit ExceededisError: User Rate Limit Exceeded Ian Boyd
0

Error: User Rate Limit Exceeded Ian Boyd
2

в mscrolib нет ничего похожего на Guid.TryParse. Согласно Reference Source, тип Guid имеет мега-сложный конструктор, который проверяет все виды форматов guid и пытается их проанализировать. Нет вспомогательного метода, который вы можете вызвать, даже с помощью отражения. Я думаю, что вы должны искать сторонние парсеры Guid или написать свой собственный.

4

is правда, что использование ошибок обходится дороже, большинство людей считают, что большинство их 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;
  }

Похожие вопросы