التحقق من محتويات السلسلة؟طول السلسلة مقابل سلسلة فارغة

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

سؤال

ما هو الأكثر فعالية للمترجم وأفضل الممارسات للتحقق مما إذا كانت السلسلة فارغة؟

  1. التحقق مما إذا كان طول السلسلة == 0
  2. التحقق مما إذا كانت السلسلة فارغة (strVar == "")

وأيضاً هل الجواب يعتمد على اللغة؟

هل كانت مفيدة؟

المحلول

نعم، يعتمد ذلك على اللغة، نظرًا لأن تخزين السلاسل يختلف بين اللغات.

  • سلاسل من نوع باسكال: Length = 0.
  • سلاسل على غرار C: [0] == 0.
  • .شبكة: .IsNullOrEmpty.

إلخ.

نصائح أخرى

في اللغات التي تستخدم سلاسل نمط C (منتهية بقيمة خالية)، مقارنة بـ "" سيكون أسرع.هذه عملية O(1)، بينما طول سلسلة النمط C هو O(n).

في اللغات التي تخزن الطول كجزء من كائن السلسلة (C#، Java، ...) يكون التحقق من الطول أيضًا هو O(1).في هذه الحالة، يكون التحقق المباشر من الطول أسرع، لأنه يتجنب الحمل الزائد لإنشاء سلسلة فارغة جديدة.

في الشباك:

string.IsNullOrEmpty( nystr );

يمكن أن تكون السلاسل فارغة، لذا فإن .Length أحيانًا يطرح NullReferenceException

في اللغات التي تستخدم سلاسل نمط C (منتهية بقيمة خالية)، ستكون المقارنة بـ "" أسرع

في الواقع، قد يكون من الأفضل التحقق مما إذا كان الحرف الأول في السلسلة هو '\0':

char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
    /* the string is empty */
}

في Perl هناك خيار ثالث، وهو أن السلسلة غير محددة.وهذا يختلف قليلاً عن المؤشر NULL في لغة C، فقط لأنك لم تحصل على خطأ تجزئة للوصول إلى سلسلة غير محددة.

في Java 1.6، لدى فئة String طريقة جديدة فارغ

توجد أيضًا مكتبة جاكرتا العامة التي تحتوي على isBlank طريقة.يتم تعريف الفراغ على أنه سلسلة تحتوي على مسافة بيضاء فقط.

بافتراض أن سؤالك هو .NET:

إذا كنت تريد التحقق من صحة السلسلة الخاصة بك ضد العدم وكذلك استخدم IsNullOrEmpty، إذا كنت تعرف بالفعل أن السلسلة الخاصة بك ليست فارغة، على سبيل المثال عند التحقق من TextBox.Text وما إلى ذلك، فلا تستخدم IsNullOrEmpty، ثم يأتي في سؤالك.
لذلك في رأيي، String.Length أقل أداءً من مقارنة السلسلة.

لقد قمت باختباره (لقد اختبرت أيضًا باستخدام C#، نفس النتيجة):

Module Module1
  Sub Main()
    Dim myString = ""


    Dim a, b, c, d As Long

    Console.WriteLine("Way 1...")

    a = Now.Ticks
    For index = 0 To 10000000
      Dim isEmpty = myString = ""
    Next
    b = Now.Ticks

    Console.WriteLine("Way 2...")

    c = Now.Ticks
    For index = 0 To 10000000
      Dim isEmpty = myString.Length = 0
    Next
    d = Now.Ticks

    Dim way1 = b - a, way2 = d - c

    Console.WriteLine("way 1 took {0} ticks", way1)
    Console.WriteLine("way 2 took {0} ticks", way2)
    Console.WriteLine("way 1 took {0} ticks more than way 2", way1 - way2)
    Console.Read()
  End Sub
End Module

نتيجة:

Way 1...
Way 2...
way 1 took 624001 ticks
way 2 took 468001 ticks
way 1 took 156000 ticks more than way 2

مما يعني أن المقارنة تستغرق أكثر من مجرد التحقق من طول السلسلة.

String.IsNullOrEmpty() يعمل فقط على .net 2.0 وما فوق، أما بالنسبة إلى .net 1/1.1، فإنني أميل إلى استخدام:

if (inputString == null || inputString == String.Empty)
{
    // String is null or empty, do something clever here. Or just expload.
}

أستخدم String.Empty بدلاً من "" لأن "" ستنشئ كائنًا، في حين أن String.Empty لن تفعل ذلك - أعلم أنه شيء صغير وتافه، لكنني لا أزال أفضل عدم إنشاء كائنات عندما لا أحتاج إليها!(مصدر)

في الواقع، أفضل طريقة لتحديد IMO هي طريقة IsNullOrEmpty() لفئة السلسلة.

http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.

تحديث:لقد افترضت أن .Net، في اللغات الأخرى، قد يكون الأمر مختلفًا.

في هذه الحالة، يكون التحقق المباشر من الطول أسرع، لأنه يتجنب الحمل الزائد لإنشاء سلسلة فارغة جديدة.

@ديريك بارك:هذا ليس صحيحا دائما."" عبارة عن سلسلة حرفية، لذلك، في Java، من المؤكد تقريبًا أنه تم تضمينها بالفعل.

بالنسبة لسلاسل C،

if (s[0] == 0)

سيكون أسرع من أي منهما

if (strlen(s) == 0)

أو

if (strcmp(s, "") == 0)

لأنك ستتجنب العبء الزائد لاستدعاء الوظيفة.

@ ناثان

في الواقع، قد يكون من الأفضل التحقق مما إذا كان الحرف الأول في السلسلة هو '\0':

لقد كدت أن أذكر ذلك، لكن انتهى بي الأمر إلى تركه منذ الاتصال strcmp() مع السلسلة الفارغة والتحقق مباشرة من الحرف الأول في السلسلة كلاهما O(1).أنت في الأساس تدفع فقط مقابل استدعاء دالة إضافية، وهو أمر رخيص جدًا.اذا أنت حقًا تحتاج إلى أفضل سرعة على الإطلاق، ومع ذلك، قم بالتأكيد بإجراء مقارنة مباشرة من أول حرف إلى 0.

بصراحة، أنا دائما استخدام strlen() == 0, ، لأن لدي أبداً قمت بكتابة برنامج حيث كان هذا في الواقع مشكلة أداء قابلة للقياس، وأعتقد أن هذه هي الطريقة الأكثر قابلية للقراءة للتعبير عن الشيك.

مرة أخرى، بدون معرفة اللغة، من المستحيل معرفة ذلك.

ومع ذلك، أوصيك باختيار الأسلوب الأكثر منطقية لمبرمج الصيانة الذي سيتبعك وسيتعين عليه صيانة عملك.

أنصحك بكتابة دالة تفعل ما تريد بشكل صريح، مثل

#define IS_EMPTY(s) ((s)[0]==0)

أو قابلة للمقارنة.الآن ليس هناك شك في أنك تقوم بالتحقق.

بعد أن قرأت هذا الموضوع، أجريت تجربة صغيرة، والتي أسفرت عن نتيجتين متميزتين ومثيرتين للاهتمام.

النظر في ما يلي.

strInstallString    "1" string

تم نسخ ما ورد أعلاه من نافذة السكان المحليين لمصحح أخطاء Visual Studio.يتم استخدام نفس القيمة في الأمثلة الثلاثة التالية.

إذا ( strInstallString == "" ) === إذا ( strInstallString == string.Empty )

فيما يلي الكود المعروض في نافذة التفكيك الخاصة بمصحح أخطاء Visual Studio 2013 لهاتين الحالتين المتطابقتين بشكل أساسي.

if ( strInstallString == "" )
003126FB  mov         edx,dword ptr ds:[31B2184h]
00312701  mov         ecx,dword ptr [ebp-50h]
00312704  call        59DEC0B0            ; On return, EAX = 0x00000000.
00312709  mov         dword ptr [ebp-9Ch],eax
0031270F  cmp         dword ptr [ebp-9Ch],0
00312716  sete        al
00312719  movzx       eax,al
0031271C  mov         dword ptr [ebp-64h],eax
0031271F  cmp         dword ptr [ebp-64h],0
00312723  jne         00312750

if ( strInstallString == string.Empty )
00452443  mov         edx,dword ptr ds:[3282184h]
00452449  mov         ecx,dword ptr [ebp-50h]
0045244C  call        59DEC0B0        ; On return, EAX = 0x00000000.
00452451  mov         dword ptr [ebp-9Ch],eax
00452457  cmp         dword ptr [ebp-9Ch],0
0045245E  sete        al
00452461  movzx       eax,al
00452464  mov         dword ptr [ebp-64h],eax
00452467  cmp         dword ptr [ebp-64h],0
0045246B  jne         00452498

إذا لم يكن ( strInstallString == string.Empty ) مختلفًا بشكل كبير

if ( strInstallString.Length == 0 )
003E284B  mov         ecx,dword ptr [ebp-50h]
003E284E  cmp         dword ptr [ecx],ecx
003E2850  call        5ACBC87E        ; On return, EAX = 0x00000001.
003E2855  mov         dword ptr [ebp-9Ch],eax
003E285B  cmp         dword ptr [ebp-9Ch],0
003E2862  setne       al
003E2865  movzx       eax,al
003E2868  mov         dword ptr [ebp-64h],eax
003E286B  cmp         dword ptr [ebp-64h],0
003E286F  jne         003E289C

من قوائم رموز الجهاز المذكورة أعلاه، والتي تم إنشاؤها بواسطة وحدة NGEN الخاصة بـ .NET Framework، الإصدار 4.5، أخلص إلى الاستنتاجات التالية.

  1. اختبار المساواة مع السلسلة الفارغة الحرفية والسلسلة الثابتة. الخاصية الفارغة في فئة System.string متطابقة لجميع الأغراض العملية.والفرق الوحيد بين مقتطفي الكود هو مصدر تعليمات الخطوة الأولى، وكلاهما عبارة عن إزاحات بالنسبة إلى ds، مما يعني أن كلاهما يشير إلى ثوابت مدمجة.

  2. اختبار المساواة مقابل السلسلة الفارغة، سواء كانت حرفية أو سلسلة. تقوم الخاصية الفارغة بإعداد استدعاء دالة ذات وسيطتين، مما يشير إلى عدم المساواة عن طريق العودة الصفر.أبني هذا الاستنتاج على اختبارات أخرى أجريتها قبل بضعة أشهر، حيث اتبعت بعض التعليمات البرمجية الخاصة بي عبر القسمة المُدارة/غير المُدارة.وفي جميع الأحوال فإن أي استدعاء يتطلب وسيطتين أو أكثر يتم وضع الوسيطة الأولى في السجل ECX، والثانية في السجل EDX.لا أتذكر كيف تم تمرير الحجج اللاحقة.ومع ذلك، بدا إعداد المكالمة أشبه بـ __fastcall وليس __stdcall.وبالمثل، فإن قيم العائد المتوقعة تظهر دائمًا في سجل EAX، وهو سجل عالمي تقريبًا.

  3. يؤدي اختبار طول السلسلة إلى إعداد استدعاء دالة ذات وسيطة واحدة، والذي يُرجع 1 (في السجل EAX)، والذي يصادف أنه طول السلسلة التي يتم اختبارها.

  4. نظرًا لأن رمز الآلة المرئي على الفور متطابق تقريبًا، فإن السبب الوحيد الذي يمكنني تخيله هو أنه يفسر الأداء الأفضل لمساواة السلسلة على طول اللدغة الذي أبلغ عنه تسلق هو أن الدالة ذات الوسيطتين التي تجري المقارنة تم تحسينها بشكل أفضل من وظيفة الوسيطة الواحدة التي تقرأ الطول من مثيل السلسلة.

خاتمة

من حيث المبدأ، أتجنب المقارنة مع السلسلة الفارغة باعتبارها حرفية، لأن السلسلة الفارغة الحرفية يمكن أن تبدو غامضة في التعليمات البرمجية المصدر.ولتحقيق هذه الغاية، قامت فئات مساعد .NET الخاصة بي منذ فترة طويلة بتعريف السلسلة الفارغة كثابت.على الرغم من أنني استخدم فارغة بالنسبة للمقارنات المباشرة والمضمنة، يحتفظ الثابت بتعريف الثوابت الأخرى التي تكون قيمتها عبارة عن سلسلة فارغة، لأنه لا يمكن تعيين ثابت فارغة كقيمته.

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

إلا أنه يطرح أيضًا سؤالًا محيرًا حول استبداله؛لماذا تتم المقارنة ضد فارغة أكثر كفاءة من اختبار طول السلسلة؟أم أن الاختبار الذي استخدمه Shinny تم إبطاله بسبب طريقة تنفيذ الحلقة؟(أجد صعوبة في تصديق ذلك، ولكن، مرة أخرى، لقد تم خداعي من قبل، وأنا متأكد من أنك قد خدعت أيضًا!)

لقد افترضت ذلك منذ فترة طويلة system.string كانت الكائنات عبارة عن سلاسل معدودة، تشبه بشكل أساسي السلسلة الأساسية (BSTR) الراسخة والتي عرفناها منذ فترة طويلة من COM.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top