كيف تتحقق في Go مما إذا كانت السلسلة النصية تحتوي على سلسلة فرعية؟ من المبتدئ إلى المحترف
\n\nهل سبق لك أن كتبت كودًا بلغة Go وتوقفت فجأة أمام سؤال بسيط: «هل تحتوي هذه السلسلة النصية على الجزء الذي أحتاجه؟» إذا كان الأمر كذلك، فأنت لست وحدك. على Stack Overflow، حصل هذا السؤال على آلاف المشاهدات، وليس من دون سبب. في Go، هناك عدة طرق أنيقة لحل هذه المشكلة، ولكن أي منها تختار؟ دعنا نفهم الأمر مرة واحدة وإلى الأبد.
\n\nفي هذه المقالة، لن نعرض لك فقط دالة strings.Contains. بل سنعلمك كيف تفكر كمطور Go: اختر الأداة المثلى لكل موقف، وتجنب المخاطر الخفية، واكتب كودًا يعمل بسرعة وبشكل يمكن التنبؤ به.
المحتويات
\n- \n
- strings.Contains — صديقك الأول \n
- strings.Index — عندما تحتاج إلى أكثر من نعم/لا \n
- ContainsAny و ContainsRune — ضبط دقيق \n
- الأداء: ماذا تختار؟ \n
- الحالات الحدية والمخاطر الخفية \n
- الخلاصة: ما يجب تذكره \n
1. strings.Contains — صديقك الأول
\n\nلنبدأ بأبسط وأشهر طريقة. دالة strings.Contains من المكتبة القياسية في Go هي ما تحتاجه في 80% من الحالات. ترجع true إذا كانت السلسلة النصية تحتوي على السلسلة الفرعية المحددة، و false في الحالة المعاكسة.
إليك كيف يبدو ذلك في كود حقيقي:
\n\npackage main\n\nimport (\n "fmt"\n "strings"\n)\n\nfunc main() {\n str := "مرحبًا، عالم Go!"\n substr := "عالم"\n\n if strings.Contains(str, substr) {\n fmt.Println("السلسلة النصية تحتوي على السلسلة الفرعية:", substr)\n } else {\n fmt.Println("لم يتم العثور على السلسلة الفرعية")\n }\n}\n\n\nالإخراج:
\nالسلسلة النصية تحتوي على السلسلة الفرعية: عالم\n\nبسيط، أليس كذلك؟ الدالة حساسة لحالة الأحرف: strings.Contains("Go", "go") سترجع false. إذا كنت بحاجة إلى تجاهل حالة الأحرف، فسيتعين عليك استخدام strings.ToLower أو strings.EqualFold.
مثال مع تجاهل حالة الأحرف
\n\nstr := "Golang is awesome"\nsubstr := "golang"\n\nif strings.Contains(strings.ToLower(str), strings.ToLower(substr)) {\n fmt.Println("تم العثور (بدون مراعاة حالة الأحرف)!")\n}\n\n\nمتى تستخدم: عندما تحتاج إلى إجابة ثنائية بسيطة — «موجود» أو «غير موجود». بدون تفاصيل إضافية.
\n\n2. strings.Index — عندما تحتاج إلى معرفة الموقع
\n\nأحيانًا لا يكفي معرفة أن السلسلة الفرعية موجودة. بل تريد أن تفهم أين توجد بالضبط. على سبيل المثال، أنت تقوم بتحليل سجل وتريد قص جزء من السلسلة النصية بعد علامة معينة. هنا يأتي دور strings.Index.
ترجع هذه الدالة فهرس أول ظهور للسلسلة الفرعية (بدءًا من 0) أو -1 إذا لم يتم العثور على السلسلة الفرعية.
\n\nstr := "خطأ: لم يتم العثور على الملف في /var/log"\nsubstr := "الملف"\n\npos := strings.Index(str, substr)\nif pos != -1 {\n fmt.Printf("تم العثور على السلسلة الفرعية في الموضع %d\\n", pos)\n fmt.Printf("السياق: %s\\n", str[pos:])\n} else {\n fmt.Println("لم يتم العثور على السلسلة الفرعية")\n}\n\n\nالإخراج:
\nتم العثور على السلسلة الفرعية في الموضع 7\nالسياق: الملف في /var/log\n\nهناك أيضًا أشكال أخرى:
\n- \n
strings.LastIndex— يبحث عن آخر ظهور \n strings.IndexAny— يبحث عن أول ظهور لأي حرف من مجموعة \n strings.IndexRune— يبحث عن حرف Unicode محدد \n
// مثال مع LastIndex\nstr := "a-b-c-d"\nlastDash := strings.LastIndex(str, "-")\nfmt.Println("آخر شرطة في الموضع:", lastDash) // 5\n\n// مثال مع IndexAny\nstr2 := "hello world"\npos := strings.IndexAny(str2, "aeiou")\nfmt.Println("أول حرف متحرك في الموضع", pos) // 1\n\n\nمتى تستخدم: عندما تحتاج إلى موقع السلسلة الفرعية لمعالجة إضافية، مثل القص أو الاستبدال.
\n\n3. ContainsAny و ContainsRune — ضبط دقيق
\n\nماذا لو كنت لا تبحث عن سلسلة فرعية كاملة، بل عن أي حرف من مجموعة معينة؟ على سبيل المثال، التحقق مما إذا كانت السلسلة النصية تحتوي على أرقام أو علامات ترقيم. هنا يأتي دور strings.ContainsAny.
str := "مرحبًا123"\n\nif strings.ContainsAny(str, "0123456789") {\n fmt.Println("السلسلة النصية تحتوي على أرقام!")\n}\n\nif strings.ContainsAny(str, "!@#$%") {\n fmt.Println("السلسلة النصية تحتوي على علامات ترقيم!")\n}\n\n\nالإخراج:
\nالسلسلة النصية تحتوي على أرقام!\n\nوهناك أيضًا strings.ContainsRune للبحث عن حرف Unicode واحد:
str := "مرحبًا 🌍"\nruneToFind := '🌍'\n\nif strings.ContainsRune(str, runeToFind) {\n fmt.Println("تم العثور على الرمز التعبيري!")\n}\n\n\nمتى تستخدم: عندما تحتاج إلى التحقق من وجود أي حرف من مجموعة (ContainsAny) أو حرف محدد (ContainsRune).
\n\n4. الأداء: ماذا تختار؟
\n\nالآن بعد أن تعرفت على جميع الطرق، ربما تتساءل: أي منها أسرع؟ الإجابة المختصرة: strings.Contains و strings.Index لهما نفس الأداء تقريبًا، حيث أن الأول هو مجرد غلاف (wrapper) للثاني. كلاهما يستخدم خوارزمية بحث فعالة (Rabin-Karp للسلاسل القصيرة و Two-Way للسلاسل الطويلة).
ومع ذلك، إذا كنت تبحث عن سلسلة فرعية قصيرة جدًا (حرف واحد)، فقد يكون strings.ContainsRune أو strings.IndexRune أسرع قليلاً. ولكن في معظم الحالات، الفرق ضئيل.
نصيحة مهمة: لا تقلق بشأن التحسين المبكر. اختر الطريقة التي تجعل كودك أكثر وضوحًا. إذا كنت تحتاج فقط إلى التحقق من الوجود — استخدم strings.Contains. إذا كنت تحتاج إلى الموقع — استخدم strings.Index. المحول البرمجي (compiler) والمكتبة القياسية يعملان بشكل جيد بما فيه الكفاية.
5. الحالات الحدية والمخاطر الخفية
\n\nحتى مع هذه الدوال البسيطة، هناك بعض التفاصيل الدقيقة التي يجب معرفتها:
\n\nالسلسلة الفرعية الفارغة
\nهل تعلم أن strings.Contains("anything", "") ترجع دائمًا true؟ وذلك لأن السلسلة الفارغة تعتبر جزءًا من أي سلسلة نصية. قد يكون هذا غير متوقع للمبتدئين.
if strings.Contains("Go", "") {\n fmt.Println("هذا سيعمل دائمًا!") // سيتم تنفيذه\n}\n\n\nحساسية حالة الأحرف
\nكما ذكرنا سابقًا، جميع الدوال حساسة لحالة الأحرف. إذا كنت بحاجة إلى بحث غير حساس لحالة الأحرف، قم بتحويل كلا السلسلتين إلى حالة واحدة باستخدام strings.ToLower أو strings.ToUpper.
Unicode والرموز التعبيرية
\nGo تتعامل بشكل ممتاز مع Unicode. تعمل strings.Contains و strings.Index بشكل صحيح مع السلاسل النصية متعددة البايت (UTF-8). ومع ذلك، تذكر أن الفهرس الذي ترجعه strings.Index هو فهرس بايت، وليس فهرس حرف (rune). إذا كنت تتعامل مع أحرف غير ASCII، فقد لا يتطابق فهرس البايت مع عدد الأحرف المرئية.
str := "مرحبًا"\nfmt.Println(strings.Index(str, "ب")) // 4 (فهرس بايت، وليس 2)\n\n\n6. الخلاصة: ما يجب تذكره
\n\nدعنا نلخص كل ما تعلمناه:
\n\n- \n
- strings.Contains — للتحقق البسيط من الوجود (نعم/لا) \n
- strings.Index — عندما تحتاج إلى موقع السلسلة الفرعية \n
- strings.ContainsAny — للتحقق من وجود أي حرف من مجموعة \n
- strings.ContainsRune — للبحث عن حرف Unicode واحد \n
- تذكر: السلسلة الفارغة موجودة دائمًا، وجميع الدوال حساسة لحالة الأحرف، والفهارس هي فهارس بايت \n
الآن أنت تعرف كل ما تحتاجه للعمل مع السلاسل النصية في Go بثقة. اختر الأداة المناسبة لمهمتك، واكتب كودًا نظيفًا وفعالًا. حظًا سعيدًا في البرمجة!