例外をスローせずに文字列が guid であるかどうかをテストしますか?

StackOverflow https://stackoverflow.com/questions/104850

  •  01-07-2019
  •  | 
  •  

質問

文字列を 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

クリスチャンK だけを捕まえる良いアイデアを思いつきました 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 頻繁に使用されている Web サーバーを実行している可能性があります。ポストバックされたデータの有効性を確認したいと考えています。無効なデータが必要以上に 2 ~ 3 桁多くのリソースを占有することは望ましくありません。

例 3 ユーザーが入力した検索式を解析している可能性があります。

enter image description here

ユーザーが 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

追記質問を正当化する必要はありません。

役に立ちましたか?

解決

パフォーマンスのベンチマーク

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

結論:文字列が guid であるかどうかを確認する必要があり、パフォーマンスを重視する場合は、COM 相互運用機能を使用します。

文字列表現の GUID を GUID に変換する必要がある場合は、次を使用します。

new Guid(someString);

他のヒント

.net 4.0 が利用可能になると、使用できるようになります Guid.TryParse().

これは気に入らないでしょうが、例外のキャッチが遅くなるのはなぜだと思いますか?

GUID の解析試行が成功した場合と比較して、何回失敗すると予想されますか?

私のアドバイスは、作成した関数を使用してコードをプロファイリングすることです。この機能が本当にホットスポットであることがわかった場合 それから 修正しますが、その前に修正してください。

.NET 4.0 では次のように記述できます。

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

少なくとも次のように書き換えます。

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

SEHException、ThreadAbortException、またはその他の致命的または無関係なもので「無効な GUID」と表示したくないでしょう。

アップデート:.NET 4.0 以降、Guid で使用できる新しいメソッドのセットが追加されました。

実際には、これらを使用する必要があります (内部的に try-catch を使用して「単純に」実装されていないという事実だけがあればですが)。

相互運用は、単に例外をキャッチするよりも遅くなります。

10,000 人の GUID とともに幸せな道を歩みましょう:

Exception:    26ms
Interop:   1,201ms

不幸な道で:

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

より安定していますが、一貫して遅くなります。未処理の例外でのみ中断するようにデバッガを構成したほうが良いようです。

さて、必要な正規表現は次のとおりです...

^[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 を受け取っていないことを祈ります。

使いやすさの理由から - デバッガーがポップアップします

try/catch アプローチを使用する場合は、[System.Diagnostics.DebuggerHidden] 属性を追加して、スロー時にブレークするように設定した場合でもデバッガーがブレークしないようにすることができます。

それと同時に エラーを使用するとコストが高くなるのは事実ですが、ほとんどの人は 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;
  }

私も同様の状況に遭遇しましたが、無効な文字列の長さが 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;
    }
}

私の知る限り、mscrolib には Guid.TryParse のようなものはありません。参照元によると、Guid 型には、あらゆる種類の guid 形式をチェックして解析しようとする非常に複雑なコンストラクターがあります。リフレクション経由であっても呼び出すことができるヘルパー メソッドはありません。サードパーティのGuidパーサーを探すか、独自に作成する必要があると思います。

RegEx または健全性チェックを行うカスタム コードを介して潜在的な GUID を実行し、文字列が少なくとも GUID のように見え、有効な文字のみで構成されていることを確認します (おそらく、全体の形式に適合しているように見えます)。健全性チェックに合格しない場合はエラーが返されます。これにより、おそらく無効な文字列の大部分が除去されるでしょう。

次に、上記のように文字列を変換しますが、サニティ チェックを通過したいくつかの無効な文字列に対する例外は引き続きキャッチされます。

Jon Skeet は、(TryParse がフレームワークに組み込まれる前に) Int の解析に関して同様の分析を行いました。 文字列を Int32 に変換できるかどうかを確認する

ただし、 アンソニー・ジョーンズ おそらくこれについて心配する必要はないことを示しています。

 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
  • リフレクターを入手
  • Guid の .ctor(String) をコピーして貼り付けます
  • 出現するすべての「throw new ...」を「return false」に置き換えます。

Guid の ctor はほとんどコンパイルされた正規表現です。そうすることで、例外のオーバーヘッドなしでまったく同じ動作が得られます。

  1. これはリバースエンジニアリングに該当しますか?私はそう思うので、違法である可能性があります。
  2. GUID 形式が変更されると壊れます。

さらにクールな解決策は、オンザフライで「throw new」を置き換えることによって、メソッドを動的に計測することです。

私は上記に投稿された GuidTryParse リンクに投票します。 ジョン または同様のソリューション (IsProbivelyGuid)。私は変換ライブラリ用にそのようなものを書きます。

この質問がこれほど複雑になるのはまったくばかげていると思います。GUID が null の場合は、「is」または「as」キーワードが適切です。しかし、何らかの理由で、SQL Server は問題なくても、.NET は問題ありません。なぜ?Guid.Empty の値は何ですか?これは .NET の設計によって引き起こされた愚かな問題であり、言語の慣習が踏みにじられると本当にイライラします。これまでのところ、フレームワークが適切に処理しないため、COM 相互運用を使用するのが最もパフォーマンスの高い答えですか?「この文字列はGUIDになりますか?」答えるのが簡単な質問であるべきです。

アプリがインターネット上に公開されるまでは、スローされる例外に依存しても問題ありません。その時点で、私はサービス拒否攻撃の準備を整えただけでした。たとえ「攻撃」を受けなかったとしても、一部の yahoo がその URL を悪用しようとしていたり​​、おそらくマーケティング部門が不正なリンクを送信したりすることはわかっています。その場合、アプリケーションはかなり大きなパフォーマンスの低下を引き起こす可能性があります。起こってはいけない問題を処理するためにコードを書いたわけではないので、サーバーがダウンしてしまいましたが、必ず起こることは誰もが知っています。

これにより、「例外」の境界線が少し曖昧になりますが、要するに、問題が頻繁ではないとしても、アプリケーションがすべてのキャッチを処理するためにクラッシュするような問題が短期間に十分に発生する可能性がある場合は、例外をスローするのが適切だと思います。悪いフォーム。

ザレイジ3K

TypeOf ctype(myvar,Object) が GUID の場合 .....

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

C# の拡張メソッドを使用する

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top