التحقق من محتويات السلسلة؟طول السلسلة مقابل سلسلة فارغة
-
08-06-2019 - |
سؤال
ما هو الأكثر فعالية للمترجم وأفضل الممارسات للتحقق مما إذا كانت السلسلة فارغة؟
- التحقق مما إذا كان طول السلسلة == 0
- التحقق مما إذا كانت السلسلة فارغة (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، فقط لأنك لم تحصل على خطأ تجزئة للوصول إلى سلسلة غير محددة.
بافتراض أن سؤالك هو .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، أخلص إلى الاستنتاجات التالية.
اختبار المساواة مع السلسلة الفارغة الحرفية والسلسلة الثابتة. الخاصية الفارغة في فئة System.string متطابقة لجميع الأغراض العملية.والفرق الوحيد بين مقتطفي الكود هو مصدر تعليمات الخطوة الأولى، وكلاهما عبارة عن إزاحات بالنسبة إلى ds، مما يعني أن كلاهما يشير إلى ثوابت مدمجة.
اختبار المساواة مقابل السلسلة الفارغة، سواء كانت حرفية أو سلسلة. تقوم الخاصية الفارغة بإعداد استدعاء دالة ذات وسيطتين، مما يشير إلى عدم المساواة عن طريق العودة الصفر.أبني هذا الاستنتاج على اختبارات أخرى أجريتها قبل بضعة أشهر، حيث اتبعت بعض التعليمات البرمجية الخاصة بي عبر القسمة المُدارة/غير المُدارة.وفي جميع الأحوال فإن أي استدعاء يتطلب وسيطتين أو أكثر يتم وضع الوسيطة الأولى في السجل ECX، والثانية في السجل EDX.لا أتذكر كيف تم تمرير الحجج اللاحقة.ومع ذلك، بدا إعداد المكالمة أشبه بـ __fastcall وليس __stdcall.وبالمثل، فإن قيم العائد المتوقعة تظهر دائمًا في سجل EAX، وهو سجل عالمي تقريبًا.
يؤدي اختبار طول السلسلة إلى إعداد استدعاء دالة ذات وسيطة واحدة، والذي يُرجع 1 (في السجل EAX)، والذي يصادف أنه طول السلسلة التي يتم اختبارها.
نظرًا لأن رمز الآلة المرئي على الفور متطابق تقريبًا، فإن السبب الوحيد الذي يمكنني تخيله هو أنه يفسر الأداء الأفضل لمساواة السلسلة على طول اللدغة الذي أبلغ عنه تسلق هو أن الدالة ذات الوسيطتين التي تجري المقارنة تم تحسينها بشكل أفضل من وظيفة الوسيطة الواحدة التي تقرأ الطول من مثيل السلسلة.
خاتمة
من حيث المبدأ، أتجنب المقارنة مع السلسلة الفارغة باعتبارها حرفية، لأن السلسلة الفارغة الحرفية يمكن أن تبدو غامضة في التعليمات البرمجية المصدر.ولتحقيق هذه الغاية، قامت فئات مساعد .NET الخاصة بي منذ فترة طويلة بتعريف السلسلة الفارغة كثابت.على الرغم من أنني استخدم فارغة بالنسبة للمقارنات المباشرة والمضمنة، يحتفظ الثابت بتعريف الثوابت الأخرى التي تكون قيمتها عبارة عن سلسلة فارغة، لأنه لا يمكن تعيين ثابت فارغة كقيمته.
يسوي هذا التمرين، مرة واحدة وإلى الأبد، أي قلق قد يكون لدي بشأن تكلفة المقارنة بأي منهما، إن وجدت فارغة أو الثابت الذي تحدده فصولي المساعدة.
إلا أنه يطرح أيضًا سؤالًا محيرًا حول استبداله؛لماذا تتم المقارنة ضد فارغة أكثر كفاءة من اختبار طول السلسلة؟أم أن الاختبار الذي استخدمه Shinny تم إبطاله بسبب طريقة تنفيذ الحلقة؟(أجد صعوبة في تصديق ذلك، ولكن، مرة أخرى، لقد تم خداعي من قبل، وأنا متأكد من أنك قد خدعت أيضًا!)
لقد افترضت ذلك منذ فترة طويلة system.string كانت الكائنات عبارة عن سلاسل معدودة، تشبه بشكل أساسي السلسلة الأساسية (BSTR) الراسخة والتي عرفناها منذ فترة طويلة من COM.