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. אין קיצוץ שקט, בניגוד ל-slices של 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 בלי לכלול אותו, כך שהמספר השני הוא אינדקס סיום, לא אורך:
// 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 עובר את הסוף. הסיבה הנפוצה ביותר היא אורך שחושב עבור מחרוזת ארוכה יותר, או תוצאה -1 של IndexOf שמשמשת כהתחלה. הגבילו את האורך עם 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 עצמו כהתחלה, וזה מחזיר מחרוזת ריקה.