Проверить, является ли строка указателем без исключения?

Я хочу попытаться преобразовать строку в 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 недействительны (?)


Обновление 1

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


Обновление 2

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

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

Пример 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

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

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

Проверить, является ли строка указателем без исключения?

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


Обновление 3 - тесты производительности

Протестируйте преобразование 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. Мне не нужно было оправдывать вопрос.

Почему это вики сообщества?

Jeff 13.03.2010 18:32

Ты прав; вы должны нет, чтобы оправдать вопрос. Однако я с интересом прочитал обоснование (так как оно очень похоже на то, почему я здесь это читаю). Итак, спасибо за большое оправдание.

b w 10.08.2010 00:19

@Jeff, вероятно, потому что OP редактировал его более 10 раз - см. мета на вики сообщества

Marijn 06.07.2011 22:13

Продолжайте искать на этой странице решения с Guid.TryParse или Guid.TryParseExact. С .NET 4.0 + вышеупомянутое решение не самое элегантное

dplante 28.02.2014 20:02

@dplante Когда я изначально задал вопрос в 2008 году, 4.0 не было. Вот почему вопрос и принятый ответ такие, какие они есть.

Ian Boyd 20.05.2014 22:15

Я не знаю ответа на этот вопрос, но, как бы то ни было, вы правы, что не хотите использовать здесь блок try / catch. Требуется много вычислений, чтобы перевесить затраты на перехват исключения, плюс попытка / отлов не подходит для обычного потока программы! Конечно, если вам не нужно перехватывать множество исключений в этом коде, это не так уж важно.

wprl 19.09.2008 23:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
181
6
74 030
18
Перейти к ответу Данный вопрос помечен как решенный

Ответы 18

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

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

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

Однако, как указывает ЭнтониУДжонс, вам, вероятно, не стоит об этом беспокоиться.

Вам это не понравится, но почему вы думаете, что перехват исключения будет медленнее?

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

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

Хороший ответ, преждевременная оптимизация - это корень всех зол.

Kev 19.09.2008 23:45

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

Ian Boyd 22.09.2008 18:32

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

AnthonyWJones 23.09.2008 14:01

Теперь я знаю, что чувствует Раймонд Чен.

Ian Boyd 24.10.2008 00:37

@Kev "Хороший ответ, преждевременная оптимизация - корень всех зол". Я согласен, Кев, нет необходимости оптимизировать что-то, что в относительном выражении ничего не будет стоить. Это зависит от объема того, что вы делаете, но в этом случае я думаю, что решение PInvoke является излишним, а не попыткой улова.

Doctor Jones 03.12.2008 19:24

«Сколько неудачных попыток разобрать GUID вы ожидаете по сравнению с успешными?» Я ожидаю, что каждая строка, которую я анализирую, не будет GUID. Смотрите рутирование папки NSE: msdn.microsoft.com/en-us/library/cc144096(VS.85).aspx

Ian Boyd 27.05.2009 15:48

Исключение следует использовать в ИСКЛЮЧИТЕЛЬНЫХ случаях, означающих: не управляется разработчиком. Я противник метода Microsoft «все исключения» для управления ошибками. Правила защитного программирования. Пожалуйста, разработчики фреймворка Microsoft рассмотрите возможность добавления TryParse в класс Guid.

Mose 29.01.2010 12:11

в ответ на мой собственный комментарий => Guid.TryParse был добавлен в framework 4.0 --- msdn.microsoft.com/en-us/library/… --- спасибо MS за такую ​​быструю реакцию;)

Mose 29.01.2010 12:28

Что ж, вот регулярное выражение, которое вам понадобится ...

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

Гм, идентификаторы GUID IIRC, которые генерируются из отметки времени, обычно считаются плохой идеей, а другой тип (тип 4) полностью случайен

BCS 25.05.2009 21:36

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

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

"-" "{" "}" ("и") "не являются допустимыми шестнадцатеричными символами, но допустимы в строке guid.

Preston Guillot 18.11.2008 20:30

и этот код будет работать отлично, если строка ввода guid содержит эти не шестнадцатеричные символы

rupello 23.05.2009 00:14

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

Я бы хотя бы переписал это так:

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

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

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

  • Guid.TryParse
  • Guid.TryParseExact

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

Ответ принят как подходящий

Тесты производительности

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.

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

new Guid(someString);

Вы запускали их с включенным или выключенным отладчиком? Производительность генерации исключений повышена в несколько раз без подключения отладчика.

Daniel T. 15.01.2010 03:41

Спасибо. Я сам собирался задать этот вопрос. Рад, что нашел твой ответ.

David 12.04.2010 23:25

Я создал новый файл с именем PInvoke.cs с фрагментом кода PInvoke пространства имен сверху, но я не могу заставить код работать. Когда я отлаживаю, я вижу, что результат CLSIDFromString ВСЕГДА отрицательный. Я попытался изменить вызывающую строку на: int hresult = PInvoke.ObjBase.CLSIDFromString (Guid.NewGuid (). ToString (), out value); но он все равно всегда отрицательный. Что я делаю не так?

JALLRED 21.02.2014 01:14

Взаимодействие медленнее, чем просто перехват исключения:

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

Exception:    26ms
Interop:   1,201ms

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

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

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

"ваш отладчик останавливался только на необработанных исключениях" Не вариант.

Ian Boyd 27.05.2009 15:40

@Ian Boyd - Если вы используете какую-либо из редакций VS (включая Express), это вариант является. msdn.microsoft.com/en-us/library/038tzxdw.aspx.

Mark Brackett 27.05.2009 16:59

Я имел в виду, что это невыполнимый вариант. Мол, «Неудача - это не вариант». Это является вариант, но я не собираюсь использовать его.

Ian Boyd 27.05.2009 21:50

если TypeOf ctype (myvar, Object) является Guid, то .....

for usability reasons - the debugger pops up

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

  • Получить рефлектор
  • copy'n'paste Guid's .ctor (строка)
  • замените каждое появление «throw new ...» на «return false».

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

  1. Это реверс-инжиниринг? Я думаю, что это так, и поэтому может быть незаконным.
  2. Сломается, если форма GUID изменится.

Еще более крутым решением было бы динамическое инструментирование метода, заменяя «бросать новый» на лету.

Я пытался украсть код у ctor, но он ссылается на многие внутренние частные классы для выполнения своей поддержки. Поверьте, это была моя первая попытка.

Ian Boyd 28.05.2009 22:48

У меня была аналогичная ситуация, и я заметил, что почти никогда не было недопустимой строки длиной 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;
    }
}

Guid принимает в своем ctor не только форму пунктирной строки. Идентификаторы GUID могут иметь окружающие фигурные скобки с тире или не содержать тире или фигурных скобок. Этот код будет генерировать ложноотрицательные результаты при использовании этими альтернативными, но также вполне допустимыми строковыми формами.

Chris Charabaruk 27.09.2009 00:16

Допустимая длина для GUID строковой формы - 32, 36 и 38 - чистый шестнадцатеричный, пунктирный и фигурные скобки с тире соответственно.

Chris Charabaruk 27.09.2009 00:18

@Chris, ваша точка зрения верна, но идея @JBrooks о разумности проверки предполагаемого GUID перед тем, как бросить вызов try / catch, имеет смысл, особенно если подозрительный ввод является обычным. Может быть, что-то вроде if (value == null || value.Length <30 || value.length> 40) {value = Guid.Empty; return false;}

b w 10.08.2010 00:41

В самом деле, это было бы лучше, хотя я бы держал диапазон более узким, 32..38, а не 30..40.

Chris Charabaruk 10.08.2010 07:07

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

Я голосую за ссылку GuidTryParse, размещенную выше Джон или аналогичное решение (IsProbablyGuid). Я напишу такой же для моей библиотеки преобразования.

Я думаю, что это совершенно неуместно, что этот вопрос настолько сложен. Ключевое слово «is» или «as» будет подходящим, ЕСЛИ Guid может иметь значение null. Но по какой-то причине, даже если SQL Server это устраивает, .NET - нет. Почему? В чем ценность Guid.Empty? Это просто глупая проблема, созданная дизайном .NET, и меня действительно беспокоит, когда соглашения языка действуют сами по себе. На данный момент наиболее эффективным ответом было использование COM-взаимодействия, потому что Framework не справляется с этим изящно? "Может ли эта строка быть GUID?" должен быть вопрос, на который легко ответить.

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

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

TheRage3K

Когда .net 4.0 станет доступным, вы можете использовать Guid.TryParse().

Еще более быстрый способ - использовать метод Guid.TryParseExact ().

user586254 30.03.2011 12:34

Если синтаксический анализ строк Guid - самая медленная часть вашего приложения, то вам повезло.

No Refunds No Returns 31.05.2017 17:46

В .NET 4.0 вы можете написать следующее:

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

Это действительно должен быть один из лучших ответов.

Tom Lint 26.08.2014 18:44

С помощью метода расширения в C#

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

Другие вопросы по теме