تعيد Substring نصًا جديدًا مكوّنًا من جزء من نص موجود. لها شكلان:
s.Substring(startIndex): منstartIndexحتى النهاية.s.Substring(startIndex, length): عددlengthمن المحارف، بدءًا منstartIndex.
تبدأ الفهارس من 0، والوسيط الثاني طول لا موضع نهاية. هذا هو الفرق عن substring(start, end) في JavaScript وsubstring(begin, end) في Java، ومصدر أخطاء إزاحة كثيرة حين تُنقل الشيفرة.
المخرجات:
4271-BLK
SHOE
4271
BLK
0
SHOE-4271-BLK
مثل كل دوال النصوص، لا تعدّل Substring الأصل؛ بل تعيد نصًا جديدًا، فأسند النتيجة.
ArgumentOutOfRangeException وكيف تتجنّبه
ترمي Substring الاستثناء ArgumentOutOfRangeException حين لا يتّسع النطاق المطلوب داخل النص: startIndex سالب، أو startIndex أكبر من Length، أو length سالب، أو startIndex + length أكبر من Length. لا يوجد اقتطاع صامت، بخلاف شرائح Python.
المخرجات:
Substring(2, 4) -> "ffee"
Substring(2, 5) -> ArgumentOutOfRangeException
Substring(6) -> ""
Substring(7) -> ArgumentOutOfRangeException
Substring(-1) -> ArgumentOutOfRangeException
Substring(0, 10) -> ArgumentOutOfRangeException
الحماية المعتادة تقييد الطول بما تبقّى من النص عبر Math.Min:
المخرجات:
ffee
ea
[]
[]
استدعاء Substring على نص null يرمي NullReferenceException لا ArgumentOutOfRangeException، ولهذا تفحص الدالة المساعدة null أولًا.
أول n محارف وآخرها
أخذ بادئة أو لاحقة هو الاستخدام الأشيع لـ Substring. كلاهما يرمي حين يكون النص أقصر من n، لذا تفحص شيفرة الإنتاج الطول:
المخرجات:
**** 1111
An Unexpecte...
Hi
Hi
حين يكون الجزء الأول من النص هو ما تريد حذفه، تكفي Substring(n) وحدها: "#FF8800".Substring(1) تساوي "FF8800". أما الدالة ذات الصلة Remove(start, count) فتفعل عكس Substring: تعيد النص بعد حذف ذلك النطاق منه.
Substring مع IndexOf
معظم الأجزاء الحقيقية يُعثر عليها ولا تُعدّ. تعيد IndexOf موضع محرف أو نص (أو -1 إن لم يكن موجودًا)، وتبحث LastIndexOf من النهاية. اجمعهما مع Substring لقصّ النص حول فاصل:
المخرجات:
maya.lopez
example.com
pdf
report.final.v2
WARN
Disk almost full
تتبع العملية الحسابية قاعدة واحدة: لأخذ النص بين الموضعين a وb (دون أي منهما)، البداية a + 1 والطول b - a - 1.
الخطر أن تعيد IndexOf القيمة -1. ترمي email.Substring(0, -1)، والأسوأ أن email.Substring(-1 + 1) تعيد النص كله بصمت. افحص دائمًا:
المخرجات:
shop.io
(none)
Split كبديل
حين يكون النص قائمة حقول بفاصل، تكون Split أبسط من سلسلة استدعاءات IndexOf وSubstring، ولا حساب فهارس فيها قد تخطئ فيه:
المخرجات:
Ana Silva
Porto
maya.lopez
beach.jpg
تحجز Split مصفوفة ونصًا لكل حقل، لذا لقصّة واحدة في حلقة ساخنة تكون IndexOf وSubstring أرخص. ولكل ما عدا ذلك تفوز سهولة القراءة. خيارات أكثر (عدة فواصل، إزالة المدخلات الفارغة، عدد أقصى) في دوال النصوص. وللأنماط مثل "الأرقام بعد ID:" يكون التعبير النمطي عادة الأداة الأوضح.
معامل النطاق (C# 8 وما بعده)
أضافت C# 8 صياغة الفهرس والنطاق، التي تعمل على النصوص والمصفوفات. تعني ^n "n من النهاية"، وa..b هو النطاق من a حتى b دون تضمين b، فالرقم الثاني فهرس نهاية لا طول:
// C# 8 and later
string sku = "SHOE-4271-BLK";
string first4 = sku[..4]; // "SHOE" same as Substring(0, 4)
string middle = sku[5..9]; // "4271" same as Substring(5, 4)
string last3 = sku[^3..]; // "BLK" same as Substring(sku.Length - 3)
string noEnds = sku[1..^1]; // "HOE-4271-BL" drop first and last
char lastChar = sku[^1]; // 'K'
على النص يُترجم النطاق إلى استدعاء Substring، فيحجز نصًا جديدًا ويرمي ArgumentOutOfRangeException في حالات الخروج عن الحدود نفسها. أيهما تستخدم مسألة أسلوب؛ والنطاقات أسهل قراءة حين يُقاس الطرفان من جهتين مختلفتين ([1..^1]).
أجزاء النص دون نسخ: Span
كل استدعاء لـ Substring يحجز نصًا جديدًا. في شيفرة التحليل الحساسة للأداء (قراءة ملفات كبيرة، معالجة طلبات كثيرة) توفّر .NET Core 2.1 وما بعدها النوع ReadOnlySpan<char>، وهو نافذة على جزء من النص الأصلي دون نسخ:
ReadOnlySpan<char> line = "2026-09-24,42.50,EUR".AsSpan();
ReadOnlySpan<char> amount = line.Slice(11, 5); // "42.50", no allocation
decimal value = decimal.Parse(amount, provider: CultureInfo.InvariantCulture);
لا يمكن تخزين Spans في حقول الأصناف العادية ولا استخدامها عبر await، لذا مكانها الشيفرة المتزامنة المحكمة. أما لشيفرة التطبيقات المعتادة فإن Substring هي الأداة الصحيحة.
أخطاء شائعة
- تمرير فهرس نهاية بدل الطول. تعني
s.Substring(2, 5)خمسة محارف من الفهرس 2، لا "من الفهرس 2 إلى 5". لأخذ النص من الفهرسaحتى الفهرسbدون تضمينه، الطول هوb - a. - عدم فحص
IndexOfللقيمة -1. إما استثناء، أو مع+ 1النص كله عن غير قصد. - افتراض أن النصوص القصيرة طويلة بما يكفي. الاسم أو الرمز أو السطر من ملف قد يكون أقصر مما تتوقع. قيّد بـ
Math.Minأو افحصLength. - تجاهل النتيجة.
s.Substring(1);في سطر وحدها لا تفعل شيئًا مفيدًا.
الأسئلة الشائعة
كيف تعمل Substring في C#؟
تعيد s.Substring(start) المحارف من الفهرس start حتى النهاية؛ وتعيد s.Substring(start, length) عددًا length من المحارف بدءًا من start. تبدأ الفهارس من 0، والوسيط الثاني طول لا فهرس نهاية: "Hello".Substring(1, 3) تساوي "ell". ولا يتغيّر النص الأصلي.
لماذا ترمي Substring الاستثناء ArgumentOutOfRangeException؟
لأن start سالب أو أكبر من طول النص، أو لأن start + length يتجاوز النهاية. السبب الأشيع طول حُسب لنص أطول، أو نتيجة IndexOf تساوي -1 استُخدمت كبداية. قيّد الطول بـ Math.Min(length, s.Length - start) وافحص نتائج IndexOf قبل استخدامها.
كيف أحصل على آخر n محارف من نص في C#؟
تعيد s.Substring(s.Length - n) آخر n محارف، بشرط أن يكون n <= s.Length. للأمان مع النصوص القصيرة استخدم s.Length <= n ? s : s.Substring(s.Length - n). في C# 8 وما بعده يمكنك أيضًا كتابة s[^n..]، التي ترمي بالطريقة نفسها حين يكون n كبيرًا جدًا.
كيف أحصل على النص بين محرفين في C#؟
اعثر على الموضعين بـ IndexOf، ثم خذ الجزء بينهما: int start = s.IndexOf('(') + 1; int end = s.IndexOf(')', start); string inside = s.Substring(start, end - start);. تحقّق أن كل IndexOf وجدت شيئًا (ليست -1) قبل استدعاء Substring. وللأنماط المعقدة يكون التعبير النمطي أوضح.
هل تبدأ فهارس Substring من الصفر في C#؟
نعم. المحرف الأول في الفهرس 0 والأخير في Length - 1. تعيد Substring(0, 3) أول ثلاثة محارف. وتمرير Length نفسه كبداية مسموح ويعيد نصًا فارغًا.