اختبار ما إذا كانت السلسلة عبارة عن دليل دون طرح استثناءات؟

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

  •  01-07-2019
  •  | 
  •  

سؤال

أريد أن أحاول تحويل سلسلة إلى دليل، ولكن لا أريد الاعتماد على التقاط الاستثناءات (

  • لأسباب تتعلق بالأداء - الاستثناءات باهظة الثمن
  • لأسباب تتعلق بقابلية الاستخدام - ينبثق مصحح الأخطاء
  • لأسباب تتعلق بالتصميم - المتوقع ليس استثنائيًا

بمعنى آخر الكود:

public static Boolean TryStrToGuid(String s, out Guid value)
{
    try
    {
        value = new Guid(s);
        return true;
    }
    catch (FormatException)
    {
        value = Guid.Empty;
        return false;
    }
}

غير مناسب.

سأحاول استخدام RegEx، ولكن بما أن المرشد يمكن أن يكون ملفوفًا بين قوسين، أو ملفوفًا بالقوس، أو غير ملفوف، فهذا يجعل الأمر صعبًا.

بالإضافة إلى ذلك، اعتقدت أن بعض القيم الإرشادية غير صالحة (؟)


تحديث 1

كريستيان ك كان فكرة جيدة للقبض فقط FormatException, ، بدلا من الكل.تم تغيير نموذج كود السؤال ليشمل الاقتراح.


تحديث 2

لماذا تقلق بشأن الاستثناءات التي تم طرحها؟هل أتوقع حقًا معرفات GUID غير صالحة كثيرًا؟

الجواب هو نعم.ولهذا السبب أستخدم TryStrToGuid - I أكون توقع بيانات سيئة.

مثال 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 ربما أقوم بتحليل تعبير بحث أدخله المستخدم.

enter image description here

إذا قاموا بإدخال المعرفات الفريدة العمومية (GUID)، فأنا أرغب في معالجتها بشكل خاص (مثل البحث على وجه التحديد عن هذا الكائن، أو تمييز مصطلح البحث المحدد وتنسيقه في نص الاستجابة.)


التحديث 3 - معايير الأداء

اختبار تحويل 10000 دليل جيد و10000 دليل سيء.

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

الحد الأدنى:إذا كنت بحاجة إلى التحقق مما إذا كانت السلسلة عبارة عن دليل، وكنت تهتم بالأداء، فاستخدم COM Interop.

إذا كنت بحاجة إلى تحويل دليل في تمثيل السلسلة إلى دليل، فاستخدم

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

لا تريد أن تقول "GUID غير صالح" على SEHException أو ThreadAbortException أو أي أشياء أخرى قاتلة أو غير ذات صلة.

تحديث:بدءًا من .NET 4.0، هناك مجموعة جديدة من الأساليب المتاحة لـ Guid:

حقًا، يجب استخدام هذه العناصر (على الأقل لحقيقة أنه لم يتم تنفيذها "بسذاجة" باستخدام أداة المحاولة داخليًا).

التشغيل المتداخل أبطأ من مجرد التقاط الاستثناء:

في الطريق السعيد مع 10.000 مرشد:

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

ولكن هذا فقط بالنسبة للمبتدئين.سيتعين عليك أيضًا التحقق من أن الأجزاء المختلفة مثل التاريخ/الوقت تقع ضمن النطاقات المقبولة.لا أستطيع أن أتخيل أن هذا أسرع من طريقة المحاولة/الالتقاط التي حددتها بالفعل.نأمل ألا تتلقى العديد من المعرفات الفريدة العمومية (GUIDs) غير الصالحة لضمان هذا النوع من الفحص!

لأسباب تتعلق بقابلية الاستخدام - ينبثق مصحح الأخطاء

إذا كنت ستتبع أسلوب المحاولة/الالتقاط، فيمكنك إضافة السمة [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;
    }
}

وبقدر ما أعرف، لا يوجد شيء مثل Guid.TryParse في mscrolib.وفقًا للمصدر المرجعي، يحتوي نوع الدليل على مُنشئ معقد للغاية يتحقق من جميع أنواع تنسيقات الأدلة ويحاول تحليلها.لا توجد طريقة مساعدة يمكنك الاتصال بها، حتى عن طريق التفكير.أعتقد أنه يتعين عليك البحث عن موزعي أدلة الطرف الثالث، أو كتابة الموزع الخاص بك.

قم بتشغيل المعرف الفريد العمومي (GUID) المحتمل من خلال RegEx أو بعض التعليمات البرمجية المخصصة التي تقوم بفحص السلامة للتأكد من أن السلسلة تبدو على الأقل مثل المعرف الفريد العمومي (GUID) وتتكون فقط من أحرف صالحة (وربما يبدو أنها تناسب التنسيق العام).إذا لم يجتاز فحص السلامة، فسيتم إرجاع خطأ - من المحتمل أن يؤدي ذلك إلى التخلص من الغالبية العظمى من السلاسل غير الصالحة.

ثم قم بتحويل السلسلة كما فعلت أعلاه، مع الاستمرار في التقاط استثناءات السلاسل القليلة غير الصالحة التي تمر عبر التحقق من السلامة.

أجرى Jon Skeet تحليلًا لشيء مشابه لتحليل Ints (قبل أن يكون TryParse في إطار العمل): التحقق من إمكانية تحويل السلسلة إلى Int32

ولكن كما أنتوني دبليو جونز أشار إلى أنك ربما لا ينبغي أن تقلق بشأن هذا.

 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
  • احصل على عاكس
  • نسخ ولصق دليل .ctor (سلسلة)
  • استبدل كل ظهور لـ "رمي جديد ..." بـ "إرجاع خطأ".

إن مُنشئ Guid عبارة عن تعبير عادي مُجمَّع إلى حد كبير، وبهذه الطريقة ستحصل على نفس السلوك تمامًا دون تحميل الاستثناء.

  1. فهل هذا يشكل هندسة عكسية؟أعتقد أنه كذلك، وعلى هذا النحو قد يكون غير قانوني.
  2. سوف ينقطع إذا تغير نموذج GUID.

الحل الأكثر روعة هو استخدام طريقة ديناميكيًا، عن طريق استبدال عبارة "رمي جديد" بسرعة.

أصوت لرابط GuidTryParse المنشور أعلاه بواسطة جون أو حل مشابه (IsProbouslyGuid).سأكتب واحدة مثل تلك لمكتبة التحويل الخاصة بي.

أعتقد أنه من السخيف تمامًا أن يكون هذا السؤال معقدًا للغاية.ستكون الكلمة الأساسية "is" أو "as" جيدة إذا كان من الممكن أن يكون المرشد خاليًا.ولكن لسبب ما، على الرغم من أن SQL Server موافق على ذلك، فإن .NET ليس كذلك.لماذا؟ما هي قيمة Guid.Empty؟هذه مجرد مشكلة سخيفة أنشأها تصميم .NET، وهي تزعجني حقًا عندما تتدخل اصطلاحات اللغة على نفسها.الإجابة الأفضل أداءً حتى الآن هي استخدام COM Interop لأن Framework لا يتعامل معها بأمان؟"هل يمكن أن تكون هذه السلسلة GUID؟" يجب أن يكون سؤالًا يسهل الإجابة عليه.

لا بأس من الاعتماد على الاستثناء الذي تم طرحه، حتى يتم نشر التطبيق على الإنترنت.في تلك المرحلة، قمت بإعداد نفسي لهجوم رفض الخدمة.حتى لو لم أتعرض "للهجوم"، أعلم أن بعض مواقع Yahoo سوف تتلاعب بعنوان URL، أو ربما يرسل قسم التسويق الخاص بي رابطًا مشوهًا، ومن ثم يجب أن يعاني تطبيقي من أداء كبير إلى حد ما يمكن أن يحققه أسفل الخادم لأنني لم أكتب الكود الخاص بي للتعامل مع مشكلة لا ينبغي أن تحدث، لكننا نعلم جميعًا أنها ستحدث.

هذا يطمس الخط قليلاً على "الاستثناء" - ولكن خلاصة القول، حتى لو كانت المشكلة نادرة، إذا كان من الممكن أن يحدث مرات كافية في فترة زمنية قصيرة بحيث يتعطل تطبيقك في خدمة عمليات الصيد من كل شيء، فأعتقد أن طرح استثناء هو شكل سيء.

TheRage3K

إذا كان 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