Substring zwraca nowy string złożony z części istniejącego. Ma dwa przeciążenia:
s.Substring(startIndex): odstartIndexdo końca.s.Substring(startIndex, length):lengthznaków, zaczynając odstartIndex.
Indeksy zaczynają się od 0, a drugi argument to długość, a nie pozycja końcowa. Na tym polega różnica względem substring(start, end) z JavaScriptu i substring(begin, end) z Javy, i to źródło wielu błędów o kilka pozycji przy przenoszeniu kodu.
Wynik:
4271-BLK
SHOE
4271
BLK
0
SHOE-4271-BLK
Jak każda metoda stringa, Substring nie zmienia oryginału; zwraca nowy string, więc przypisz wynik.
ArgumentOutOfRangeException i jak go uniknąć
Substring rzuca ArgumentOutOfRangeException, gdy żądany zakres nie mieści się w stringu: ujemny startIndex, startIndex większy niż Length, ujemne length albo startIndex + length większe niż Length. Nie ma cichego obcinania, w przeciwieństwie do wycinków w Pythonie.
Wynik:
Substring(2, 4) -> "ffee"
Substring(2, 5) -> ArgumentOutOfRangeException
Substring(6) -> ""
Substring(7) -> ArgumentOutOfRangeException
Substring(-1) -> ArgumentOutOfRangeException
Substring(0, 10) -> ArgumentOutOfRangeException
Typowym zabezpieczeniem jest ograniczenie długości do tego, co zostało ze stringa, przez Math.Min:
Wynik:
ffee
ea
[]
[]
Wywołanie Substring na stringu null rzuca NullReferenceException, a nie ArgumentOutOfRangeException, dlatego metoda pomocnicza najpierw sprawdza null.
Pierwsze i ostatnie n znaków
Pobranie prefiksu albo sufiksu to najczęstsze zastosowanie Substring. Oba rzucają wyjątek, gdy string jest krótszy niż n, więc kod produkcyjny sprawdza długość:
Wynik:
**** 1111
An Unexpecte...
Hi
Hi
Gdy chcesz odrzucić początek stringa, wystarczy samo Substring(n): "#FF8800".Substring(1) to "FF8800". Pokrewne Remove(start, count) robi odwrotność Substring: zwraca string z usuniętym tym zakresem.
Substring z IndexOf
Większość prawdziwych fragmentów się znajduje, a nie odlicza. IndexOf zwraca pozycję znaku lub stringa (albo -1, jeśli go nie ma), a LastIndexOf szuka od końca. Połącz je z Substring, żeby ciąć tekst wokół separatora:
Wynik:
maya.lopez
example.com
pdf
report.final.v2
WARN
Disk almost full
Arytmetyka opiera się na jednej zasadzie: żeby wziąć tekst między pozycjami a i b (bez żadnej z nich), początek to a + 1, a długość to b - a - 1.
Niebezpieczeństwem jest IndexOf zwracające -1. email.Substring(0, -1) rzuca wyjątek, a co gorsza, email.Substring(-1 + 1) po cichu zwraca cały string. Zawsze sprawdzaj:
Wynik:
shop.io
(none)
Split jako alternatywa
Gdy string to lista pól z separatorem, Split jest prostszy niż łańcuch wywołań IndexOf i Substring i nie ma w nim arytmetyki indeksów, w której można się pomylić:
Wynik:
Ana Silva
Porto
maya.lopez
beach.jpg
Split alokuje tablicę i po jednym stringu na pole, więc przy pojedynczym cięciu w gorącej pętli IndexOf i Substring są tańsze. We wszystkich innych przypadkach wygrywa czytelność. Więcej opcji (kilka separatorów, usuwanie pustych wpisów, maksymalna liczba części) znajdziesz w metodach stringów. Przy wzorcach typu "cyfry po ID:" najczytelniejszym narzędziem jest zwykle wyrażenie regularne.
Operator zakresu (C# 8 i nowsze)
C# 8 dodał składnię indeksów i zakresów, która działa na stringach i tablicach. ^n oznacza "n od końca", a a..b to zakres od a do b, ale bez b, więc druga liczba to indeks końcowy, a nie długość:
// 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'
Na stringu zakres kompiluje się do wywołania Substring, więc alokuje nowy string i rzuca ArgumentOutOfRangeException w tych samych przypadkach wyjścia poza zakres. Wybór to kwestia stylu; zakresy czyta się lepiej, gdy oba końce są mierzone z różnych stron ([1..^1]).
Fragmenty bez kopiowania: Span
Każde wywołanie Substring alokuje nowy string. W kodzie parsującym, gdzie liczy się wydajność (czytanie dużych plików, obsługa wielu żądań), .NET Core 2.1 i nowsze oferują ReadOnlySpan<char>, widok na część oryginalnego stringa bez kopiowania:
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);
Spanów nie można przechowywać w polach zwykłych klas ani używać przez await, więc pasują do zwartego, synchronicznego kodu. W typowym kodzie aplikacji właściwym narzędziem jest Substring.
Częste błędy
- Przekazywanie indeksu końcowego jako długości.
s.Substring(2, 5)oznacza pięć znaków od indeksu 2, a nie "od indeksu 2 do 5". Żeby wziąć tekst od indeksuado indeksubbez niego, długość wynosib - a. - Niesprawdzanie, czy
IndexOfzwrócił -1. Kończy się to wyjątkiem albo, z+ 1, przypadkowo całym stringiem. - Zakładanie, że krótkie stringi są wystarczająco długie. Imię, kod albo linia z pliku mogą być krótsze, niż się spodziewasz. Ogranicz przez
Math.Minalbo sprawdźLength. - Wyrzucanie wyniku. Samo
s.Substring(1);w osobnej linii nie robi nic użytecznego.
Najczęściej zadawane pytania
Jak działa Substring w C#?
s.Substring(start) zwraca znaki od indeksu start do końca; s.Substring(start, length) zwraca length znaków, zaczynając od start. Indeksy zaczynają się od 0, a drugi argument to długość, a nie indeks końcowy: "Hello".Substring(1, 3) to "ell". Oryginalny string się nie zmienia.
Dlaczego Substring rzuca ArgumentOutOfRangeException?
Bo start jest ujemny albo większy niż długość stringa, albo start + length wychodzi poza koniec. Najczęstszą przyczyną jest długość obliczona dla dłuższego stringa albo wynik IndexOf równy -1 użyty jako początek. Ogranicz długość przez Math.Min(length, s.Length - start) i sprawdzaj wyniki IndexOf przed użyciem.
Jak pobrać ostatnie n znaków stringa w C#?
s.Substring(s.Length - n) zwraca ostatnie n znaków, o ile n <= s.Length. Żeby było bezpiecznie dla krótkich stringów, użyj s.Length <= n ? s : s.Substring(s.Length - n). W C# 8 i nowszych możesz też napisać s[^n..], które rzuca wyjątek tak samo, gdy n jest za duże.
Jak pobrać fragment między dwoma znakami w C#?
Znajdź obie pozycje przez IndexOf, a potem weź część między nimi: int start = s.IndexOf('(') + 1; int end = s.IndexOf(')', start); string inside = s.Substring(start, end - start);. Przed wywołaniem Substring sprawdź, czy każde IndexOf coś znalazło (nie -1). Przy złożonych wzorcach wyrażenie regularne jest czytelniejsze.
Czy Substring w C# liczy od zera?
Tak. Pierwszy znak jest pod indeksem 0, a ostatni pod Length - 1. Substring(0, 3) zwraca pierwsze trzy znaki. Przekazanie samego Length jako początku jest dozwolone i zwraca pusty string.