Substring は、既存の文字列の一部からなる新しい文字列を返します。オーバーロードは2つあります。
s.Substring(startIndex):startIndexから末尾まで。s.Substring(startIndex, length):startIndexから始まるlength文字。
インデックスは0から始まり、2番目の引数は終了位置ではなく長さです。これがJavaScriptの substring(start, end) やJavaの substring(begin, end) との違いで、コードを移植したときに位置が少しずれるバグの多くの原因になります。
出力:
4271-BLK
SHOE
4271
BLK
0
SHOE-4271-BLK
すべての文字列のメソッドと同じく、Substring は元の文字列を変更せず、新しい文字列を返すので、結果を代入します。
ArgumentOutOfRangeExceptionとその避け方
要求した範囲が文字列の中に収まらないと、Substring は ArgumentOutOfRangeException を投げます。負の startIndex、Length より大きい startIndex、負の length、または Length より大きい startIndex + 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
[]
[]
null の文字列に対して Substring を呼ぶと、ArgumentOutOfRangeException ではなく NullReferenceException が投げられます。だからヘルパーは最初にnullを確認しています。
最初と最後のn文字
先頭部分や末尾部分を取り出すのは、Substring の最もよくある使い方です。どちらも文字列が n より短いと例外を投げるので、本番のコードでは長さを確認します。
出力:
**** 1111
An Unexpecte...
Hi
Hi
文字列の最初の部分を取り除きたいだけなら、Substring(n) だけで十分です:"#FF8800".Substring(1) は "FF8800" です。関連する Remove(start, count) は Substring の逆で、その範囲を取り除いた文字列を返します。
IndexOfと組み合わせたSubstring
実際の部分文字列のほとんどは、数えるのではなく見つけるものです。IndexOf は文字や文字列の位置を返し(なければ-1)、LastIndexOf は末尾から探します。これらを Substring と組み合わせて、区切り文字の前後のテキストを切り出します。
出力:
maya.lopez
example.com
pdf
report.final.v2
WARN
Disk almost full
計算は1つのルールに従います。位置 a と b の間のテキストを(どちらも含めずに)取り出すには、開始位置は a + 1、長さは b - a - 1 です。
危険なのは IndexOf が-1を返すことです。email.Substring(0, -1) は例外を投げ、さらに悪いことに、email.Substring(-1 + 1) は何も言わずに文字列全体を返します。必ず確認します。
出力:
shop.io
(none)
代替手段としてのSplit
文字列が区切り文字で区切られたフィールドのリストなら、IndexOf と Substring の呼び出しを連ねるより Split のほうが簡単で、間違えるインデックスの計算もありません。
出力:
Ana Silva
Porto
maya.lopez
beach.jpg
Split は配列とフィールドごとに1つの文字列を割り当てるので、性能が重要なループで1か所を切るだけなら、IndexOf と Substring のほうが軽くなります。それ以外では読みやすさが勝ります。その他のオプション(複数の区切り文字、空の要素の削除、最大の個数)は文字列のメソッドにあります。「ID: の後の数字」のようなパターンには、たいてい正規表現が最も明確な手段です。
範囲演算子(C# 8以降)
C# 8では、文字列と配列に使えるインデックスと範囲の構文が追加されました。^n は「末尾からn番目」を意味し、a..b は a から b の手前まで(b を含まない)の範囲なので、2番目の数は長さではなく終了インデックスです。
// 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);
Spanは通常のクラスのフィールドに格納できず、await をまたいで使うこともできないので、同期的で処理の密なコードに向いています。一般的なアプリケーションのコードでは、Substring が適切な道具です。
よくある間違い
- 長さの代わりに終了インデックスを渡す。
s.Substring(2, 5)は「インデックス2から5まで」ではなく、インデックス2からの5文字を意味します。インデックスaからインデックスbの手前までのテキストを取り出すなら、長さはb - aです。 IndexOfの-1を確認しない。 例外になるか、+ 1を付けていると、誤って文字列全体になります。- 短い文字列が十分な長さだと思い込む。 名前、コード、ファイルの行は、思ったより短いことがあります。
Math.Minで制限するか、Lengthを確認します。 - 結果を捨てる。 単独の行の
s.Substring(1);は何の役にも立ちません。
よくある質問
C#のSubstringはどう動きますか?
s.Substring(start) はインデックス start から末尾までの文字を返し、s.Substring(start, length) は start から始まる length 文字を返します。インデックスは0から始まり、2番目の引数は終了インデックスではなく長さです:"Hello".Substring(1, 3) は "ell" です。元の文字列は変更されません。
SubstringがArgumentOutOfRangeExceptionを投げるのはなぜですか?
start が負か文字列の長さより大きいか、start + length が末尾を超えているからです。最もよくある原因は、もっと長い文字列を前提に計算した長さや、-1になった IndexOf の結果を開始位置に使うことです。Math.Min(length, s.Length - start) で長さを制限し、IndexOf の結果は使う前に確認します。
C#で文字列の最後のn文字を取得するには?
n <= s.Length なら、s.Substring(s.Length - n) が最後の n 文字を返します。短い文字列でも安全にするには、s.Length <= n ? s : s.Substring(s.Length - n) を使います。C# 8以降では s[^n..] とも書けますが、n が大きすぎると同じように例外が投げられます。
C#で2つの文字の間の部分文字列を取得するには?
IndexOf で両方の位置を見つけ、その間の部分を取り出します:int start = s.IndexOf('(') + 1; int end = s.IndexOf(')', start); string inside = s.Substring(start, end - start);。Substring を呼ぶ前に、各 IndexOf が何かを見つけた(-1でない)ことを確認します。複雑なパターンには、正規表現のほうが明確です。
C#のSubstringのインデックスは0から始まりますか?
はい。最初の文字はインデックス0にあり、最後の文字は Length - 1 にあります。Substring(0, 3) は最初の3文字を返します。開始位置として Length そのものを渡すことは許されていて、空の文字列が返ります。