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

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

blockquote>

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.
blockquote>

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

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

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

180
задан 20 revs, 6 users 96% 30 May 2017 в 04:27
поделиться

16 ответов

Сравнительные тесты Производительности

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:

/// <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);
    }
}
<час>

Нижняя строка: Если необходимо проверить, является ли строка гуидом, и Вы заботитесь о производительности, используете COM Interop.

, Если необходимо преобразовать гуид в Строковом представлении к Гуиду, используйте

new Guid(someString);
107
ответ дан Ian Boyd 23 November 2019 в 06:12
поделиться
 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
1
ответ дан rupello 23 November 2019 в 06:12
поделиться

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

Тогда преобразовывают строку, как Вы имеете выше, все еще ловя исключение для нескольких недопустимых строк, которые проходят через проверку работоспособности.

Jon Skeet сделал анализ для чего-то подобного для парсинга Ints (прежде чем TryParse был в Платформе): Проверка, если строка может быть преобразована в Int32

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

2
ответ дан Community 23 November 2019 в 06:12
поделиться

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

2
ответ дан Ilya Ryzhenkov 23 November 2019 в 06:12
поделиться

Ну, вот regex, в котором Вы будете нуждаться...

^[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}(}})$

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

9
ответ дан Ian Boyd 23 November 2019 в 06:12
поделиться

Interop медленнее, чем просто ловля исключения:

В счастливом пути, с 10 000 Гуидов:

Exception:    26ms
Interop:   1,201ms

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

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

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

13
ответ дан Ian Boyd 23 November 2019 в 06:12
поделиться

Я, по крайней мере, переписал бы его как:

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

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

Обновление : Запуск с.NET 4.0, существует новый набор методов, доступных для Гуида:

Действительно, те должны использоваться (если только для факта, что они "наивно" не реализованы с помощью выгоды попытки внутренне).

21
ответ дан Christian.K 23 November 2019 в 06:12
поделиться

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

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

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

66
ответ дан AnthonyWJones 23 November 2019 в 06:12
поделиться

В то время как это верно, что использование ошибок является более дорогим, большинство людей полагает, что большинство их 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
ответ дан Josef 23 November 2019 в 06:12
поделиться

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

0
ответ дан 23 November 2019 в 06:12
поделиться

из соображений удобства использования - всплывает отладчик

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

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

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

  1. Является ли это обратной инженерией? Я думаю, что это так, и как таковое может быть незаконным.
  2. Будет нарушено, если форма GUID изменится.

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

1
ответ дан 23 November 2019 в 06:12
поделиться

I had a similar situation and I noticed that almost never was the invalid string 36 characters long. So based on this fact, I changed your code a little to get better performance while still keeping it simple.

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;
    }
}
4
ответ дан 23 November 2019 в 06:12
поделиться
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
ответ дан 23 November 2019 в 06:12
поделиться

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

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

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

1
ответ дан 23 November 2019 в 06:12
поделиться

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

.
89
ответ дан 23 November 2019 в 06:12
поделиться
Другие вопросы по тегам:

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