C# Substringの使い方を完全解説|文字列の切り出し・エラー対策・実践例までわかる
はじめに
C#で文字列の一部を取り出したいときによく使うのがSubstringメソッドです。
たとえば、次のような処理で使われます。
C#string text = "Hello, C#";
string result = text.Substring(7);
Console.WriteLine(result);
実行結果は次のとおりです。
C#C#
Substringを使うと、文字列の先頭・途中・末尾など、指定した位置から必要な部分だけを切り出せます。
一方で、startIndexやlengthの指定を間違えると、ArgumentOutOfRangeExceptionなどのエラーが発生しやすい点には注意が必要です。
この記事では、C#のSubstringの基本構文から、実践的な使い方、エラーの原因と対策、SplitやRange構文との違いまでわかりやすく解説します。
1. C#のSubstringとは?文字列を切り出す基本メソッド
Substringは、C#のstring型で使える文字列操作メソッドです。
文字列の中から、指定した位置以降の文字や、指定した文字数分だけを取り出すときに使用します。
C#string text = "abcdef";
string result = text.Substring(2);
Console.WriteLine(result);
実行結果は次のとおりです。
C#cdef
この例では、インデックス2の位置にあるcから末尾までを切り出しています。
1-1. Substringでできること
Substringでは、主に次のようなことができます。
文字列の途中から末尾までを取り出す。
C#string text = "Programming";
string result = text.Substring(3);
Console.WriteLine(result);
C#gramming
指定した位置から指定した文字数だけ取り出す。
C#string text = "Programming";
string result = text.Substring(0, 4);
Console.WriteLine(result);
C#Prog
このように、Substringは「何文字目から」「何文字分」を指定して文字列を切り出すメソッドです。
1-2. Substringを使う場面
Substringは、文字列の形式がある程度決まっている場面でよく使われます。
たとえば、固定長の会員IDから区分コードを取り出す場合です。
C#string memberId = "AB20250001";
string typeCode = memberId.Substring(0, 2);
Console.WriteLine(typeCode);
C#AB
日付文字列から年・月・日を取り出す場合にも使えます。
C#string date = "20260606";
string year = date.Substring(0, 4);
string month = date.Substring(4, 2);
string day = date.Substring(6, 2);
Console.WriteLine(year);
Console.WriteLine(month);
Console.WriteLine(day);
C#2026
06
06
このように、文字列の位置や長さが決まっているデータを扱うときにSubstringは便利です。
1-3. Substringは元の文字列を変更しない
Substringは、元の文字列を直接変更するメソッドではありません。
切り出した結果として、新しい文字列を返します。
C#string text = "HelloWorld";
string result = text.Substring(5);
Console.WriteLine(text);
Console.WriteLine(result);
実行結果は次のようになります。
C#HelloWorld
World
元のtextはHelloWorldのままです。
C#のstringは不変、つまり一度作成された文字列の内容は変更されません。SubstringやReplaceなどの文字列操作メソッドは、元の文字列を変更するのではなく、処理結果の文字列を返します。
2. Substringの基本構文と2つの使い方
Substringには、主に次の2つの使い方があります。
C#文字列.Substring(startIndex)
C#文字列.Substring(startIndex, length)
1つ目は、指定した位置から末尾までを切り出す書き方です。
2つ目は、指定した位置から指定した文字数分だけ切り出す書き方です。
2-1. Substring(startIndex):指定位置から末尾まで切り出す
Substring(startIndex)は、指定したインデックスから文字列の末尾までを切り出します。
C#string text = "CSharp";
string result = text.Substring(1);
Console.WriteLine(result);
実行結果は次のとおりです。
C#Sharp
CSharpのインデックスは次のように考えます。
C#C S h a r p
0 1 2 3 4 5
Substring(1)を指定すると、インデックス1のSから末尾までのSharpが返されます。
2-2. Substring(startIndex, length):指定位置から指定文字数を切り出す
Substring(startIndex, length)は、指定したインデックスから指定文字数分だけ切り出します。
C#string text = "CSharp";
string result = text.Substring(1, 3);
Console.WriteLine(result);
実行結果は次のとおりです。
C#Sha
インデックス1のSから、3文字分のShaを取り出しています。
2-3. startIndexとlengthの意味
Substringで重要なのが、startIndexとlengthの意味です。
startIndexは、切り出しを開始する位置です。
lengthは、切り出す文字数です。
C#string text = "abcdef";
string result = text.Substring(2, 3);
Console.WriteLine(result);
C#cde
この例では、インデックス2のcから3文字分を切り出しているため、結果はcdeになります。
注意したいのは、lengthは「終了位置」ではなく「文字数」だという点です。
C#string text = "abcdef";
// インデックス2からインデックス4まで、という意味ではない
string result = text.Substring(2, 4);
Console.WriteLine(result);
この場合は、インデックス2から4文字分を切り出すため、結果は次のようになります。
C#cdef
2-4. インデックスは0から始まる点に注意
C#の文字列インデックスは、1ではなく0から始まります。
C#string text = "ABCDE";
各文字のインデックスは次のようになります。
C#A B C D E
0 1 2 3 4
そのため、先頭の文字を取り出したい場合はSubstring(0, 1)と書きます。
C#string text = "ABCDE";
string first = text.Substring(0, 1);
Console.WriteLine(first);
C#A
2文字目を取り出したい場合は、インデックス1を指定します。
C#string text = "ABCDE";
string second = text.Substring(1, 1);
Console.WriteLine(second);
C#B
「1文字目だから1を指定する」と考えてしまうと、意図した位置とずれてしまうため注意しましょう。
3. Substringの使い方をサンプルコードで解説
ここからは、Substringの使い方を具体的なサンプルコードで見ていきます。
3-1. 文字列の先頭から切り出す
文字列の先頭から一定の文字数だけ取り出すには、startIndexに0を指定します。
C#string text = "HelloWorld";
string result = text.Substring(0, 5);
Console.WriteLine(result);
実行結果は次のとおりです。
C#Hello
先頭から5文字分を切り出しているため、Helloが取得できます。
先頭の1文字だけを取得する場合は、次のように書きます。
C#string text = "HelloWorld";
string result = text.Substring(0, 1);
Console.WriteLine(result);
C#H
3-2. 文字列の途中から切り出す
文字列の途中から末尾までを取り出す場合は、Substring(startIndex)を使います。
C#string text = "HelloWorld";
string result = text.Substring(5);
Console.WriteLine(result);
C#World
インデックス5のWから末尾までが切り出されています。
途中から指定文字数だけ取り出したい場合は、Substring(startIndex, length)を使います。
C#string text = "HelloWorld";
string result = text.Substring(3, 4);
Console.WriteLine(result);
C#loWo
3-3. 文字列の末尾を切り出す
文字列の末尾から数文字を取得したい場合は、文字列の長さを表すLengthを使って開始位置を計算します。
C#string text = "HelloWorld";
string result = text.Substring(text.Length - 5);
Console.WriteLine(result);
実行結果は次のとおりです。
C#World
text.Lengthは10です。
末尾5文字を取り出す場合、開始位置は10 - 5で5になります。
C#string text = "HelloWorld";
Console.WriteLine(text.Length);
Console.WriteLine(text.Substring(text.Length - 5));
C#10
World
末尾1文字だけを取得したい場合は、次のように書けます。
C#string text = "HelloWorld";
string last = text.Substring(text.Length - 1);
Console.WriteLine(last);
C#d
ただし、空文字に対してtext.Length - 1を使うと-1になり、エラーになります。実務では、事前に文字列の長さを確認することが重要です。
3-4. 指定した文字数だけ切り出す
指定した位置から指定文字数だけ切り出すには、Substring(startIndex, length)を使います。
C#string text = "1234567890";
string result = text.Substring(2, 4);
Console.WriteLine(result);
C#3456
インデックス2の3から4文字分を切り出しています。
郵便番号や商品コードのように、文字数が固定されているデータを扱う場合に便利です。
C#string postalCode = "1000001";
string firstPart = postalCode.Substring(0, 3);
string secondPart = postalCode.Substring(3, 4);
Console.WriteLine(firstPart);
Console.WriteLine(secondPart);
C#100
0001
3-5. 日本語文字列を切り出す場合の例
Substringは日本語文字列にも使用できます。
C#string text = "東京都新宿区";
string result = text.Substring(0, 3);
Console.WriteLine(result);
実行結果は次のとおりです。
C#東京都
途中から切り出すこともできます。
C#string text = "東京都新宿区";
string result = text.Substring(3);
Console.WriteLine(result);
C#新宿区
ただし、C#のSubstringは内部的にはUTF-16のchar単位で処理します。
通常の日本語の漢字・ひらがな・カタカナであれば直感的に扱えることが多いですが、絵文字や一部の特殊文字は見た目の1文字が複数のcharで構成される場合があります。
C#string text = "A😀B";
Console.WriteLine(text.Length);
このような文字列では、見た目の文字数とLengthの値が一致しないことがあります。
ユーザーが入力した文章を「見た目の文字単位」で正確に切り出す必要がある場合は、Substringだけでなく、文字要素を扱う方法も検討しましょう。
4. 実践でよく使うSubstringの活用例
Substringは、実務でもさまざまな場面で使われます。
ここでは、よくある活用例を紹介します。
4-1. 固定長のコードやIDを切り出す
固定長のコードから一部の情報を取り出す場合、Substringはとても便利です。
C#string orderCode = "EC202606060001";
string serviceCode = orderCode.Substring(0, 2);
string datePart = orderCode.Substring(2, 8);
string serialNumber = orderCode.Substring(10, 4);
Console.WriteLine(serviceCode);
Console.WriteLine(datePart);
Console.WriteLine(serialNumber);
実行結果は次のとおりです。
C#EC
20260606
0001
この例では、注文コードを次のように分解しています。
C#EC 20260606 0001
固定長のデータであれば、開始位置と文字数を指定するだけで必要な情報を取り出せます。
ただし、実際のシステムでは、想定より短い文字列が渡される可能性もあります。Substringを使う前に、文字列の長さを確認することが大切です。
4-2. 日付文字列から年・月・日を取り出す
yyyyMMdd形式の日付文字列から、年・月・日を取り出す例です。
C#string date = "20260606";
string year = date.Substring(0, 4);
string month = date.Substring(4, 2);
string day = date.Substring(6, 2);
Console.WriteLine($"{year}年{month}月{day}日");
実行結果は次のとおりです。
C#2026年06月06日
日付文字列の形式が固定されている場合は、Substringで簡単に分解できます。
ただし、日付として正しいかどうかまで確認したい場合は、DateTime.TryParseExactなどを使うほうが安全です。
C#using System;
using System.Globalization;
string date = "20260606";
if (DateTime.TryParseExact(
date,
"yyyyMMdd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime parsedDate))
{
Console.WriteLine(parsedDate.ToString("yyyy年MM月dd日"));
}
else
{
Console.WriteLine("日付形式が正しくありません。");
}
単純に文字列の一部を取り出したいだけならSubstring、日付としての妥当性も確認したいならDateTime関連のメソッドを使いましょう。
4-3. ファイル名や拡張子の一部を取得する
ファイル名から拡張子を取り出す場合も、Substringを使えます。
C#string fileName = "report.pdf";
int dotIndex = fileName.LastIndexOf(".");
string extension = fileName.Substring(dotIndex + 1);
Console.WriteLine(extension);
実行結果は次のとおりです。
C#
LastIndexOf(".")で最後のドットの位置を取得し、その次の位置から末尾までをSubstringで切り出しています。
ファイル名部分だけを取り出す場合は、次のように書けます。
C#string fileName = "report.pdf";
int dotIndex = fileName.LastIndexOf(".");
string nameWithoutExtension = fileName.Substring(0, dotIndex);
Console.WriteLine(nameWithoutExtension);
C#report
ただし、ファイルパスを扱う場合は、System.IO.Pathクラスを使うほうが安全です。
C#using System.IO;
string fileName = "report.pdf";
string extension = Path.GetExtension(fileName);
string nameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
Console.WriteLine(extension);
Console.WriteLine(nameWithoutExtension);
C#
report
単純な文字列処理ならSubstringで十分ですが、ファイルパスや拡張子を正確に扱う場合はPathクラスを使うのがおすすめです。
4-4. URLやパスの一部を抽出する
URLの一部を取り出すときにも、SubstringとIndexOfを組み合わせられます。
C#string url = "https://example.com/products/12345";
int index = url.IndexOf("/products/");
string productId = url.Substring(index + "/products/".Length);
Console.WriteLine(productId);
実行結果は次のとおりです。
C#12345
/products/の位置をIndexOfで探し、その後ろから末尾までをSubstringで切り出しています。
パスの最後の部分だけを取得したい場合は、LastIndexOfが便利です。
C#string path = "/users/tanaka/documents/report.pdf";
int slashIndex = path.LastIndexOf("/");
string fileName = path.Substring(slashIndex + 1);
Console.WriteLine(fileName);
C#report.pdf
ただし、URLを本格的に解析する場合はUriクラスを使うほうが安全です。
C#Uri uri = new Uri("https://example.com/products/12345?ref=mail");
Console.WriteLine(uri.Host);
Console.WriteLine(uri.AbsolutePath);
Console.WriteLine(uri.Query);
C#example.com
/products/12345
?ref=mail
Substringは手軽ですが、URLやパスの仕様をすべて自前で処理しようとすると複雑になります。用途に応じて専用クラスと使い分けましょう。
4-5. IndexOfと組み合わせて特定文字以降を切り出す
Substringは、IndexOfと組み合わせることで、特定の文字や文字列の後ろを切り出せます。
たとえば、メールアドレスからドメイン部分を取得する例です。
C#string email = "user@example.com";
int atIndex = email.IndexOf("@");
string domain = email.Substring(atIndex + 1);
Console.WriteLine(domain);
実行結果は次のとおりです。
C#example.com
@より前のユーザー名部分を取得する場合は、次のように書けます。
C#string email = "user@example.com";
int atIndex = email.IndexOf("@");
string userName = email.Substring(0, atIndex);
Console.WriteLine(userName);
C#user
ただし、IndexOfは指定した文字列が見つからない場合に-1を返します。
そのままSubstringに使うとエラーになるため、実務では必ずチェックしましょう。
C#string email = "userexample.com";
int atIndex = email.IndexOf("@");
if (atIndex >= 0)
{
string domain = email.Substring(atIndex + 1);
Console.WriteLine(domain);
}
else
{
Console.WriteLine("@が見つかりません。");
}
5. Substringで発生しやすいエラーと原因
Substringは便利ですが、指定する範囲を間違えると例外が発生します。
特によく発生するのがArgumentOutOfRangeExceptionです。
5-1. ArgumentOutOfRangeExceptionが発生するケース
ArgumentOutOfRangeExceptionは、引数に指定した値が有効な範囲外だった場合に発生します。
たとえば、次のコードはエラーになります。
C#string text = "abc";
string result = text.Substring(5);
文字列abcの長さは3です。
存在しないインデックス5から切り出そうとしているため、範囲外エラーになります。
また、次のコードもエラーになります。
C#string text = "abc";
string result = text.Substring(1, 5);
インデックス1から5文字分を切り出そうとすると、文字列の末尾を超えてしまいます。
5-2. startIndexが文字列の長さを超えている
startIndexは、0以上かつ文字列の長さ以下である必要があります。
C#string text = "abc";
Console.WriteLine(text.Length);
C#3
この文字列で有効なインデックスは0、1、2です。
Substring(3)はどうなるでしょうか。
C#string text = "abc";
string result = text.Substring(3);
Console.WriteLine($"'{result}'");
実行結果は空文字です。
C#''
startIndexが文字列の長さと同じ場合、末尾のさらに後ろから0文字を切り出す扱いになるため、空文字が返ります。
一方、startIndexが文字列の長さを超えるとエラーになります。
C#string text = "abc";
string result = text.Substring(4);
これはArgumentOutOfRangeExceptionになります。
5-3. lengthの指定範囲が文字列を超えている
Substring(startIndex, length)では、startIndex + lengthが文字列の長さを超えないようにする必要があります。
C#string text = "abcdef";
string result = text.Substring(4, 3);
インデックス4のeから3文字分を取得しようとすると、e、fの2文字しか残っていません。
そのため、範囲外エラーになります。
正しくは次のように指定します。
C#string text = "abcdef";
string result = text.Substring(4, 2);
Console.WriteLine(result);
C#ef
5-4. 負の数を指定している
startIndexやlengthに負の数を指定すると、ArgumentOutOfRangeExceptionが発生します。
C#string text = "abcdef";
string result1 = text.Substring(-1);
string result2 = text.Substring(1, -2);
どちらも不正な指定です。
特に、末尾から文字を取得しようとして、次のようなコードを書く場合には注意が必要です。
C#string text = "";
string last = text.Substring(text.Length - 1);
空文字の場合、text.Lengthは0です。
そのため、text.Length - 1は-1になり、エラーになります。
末尾の文字を取得する場合は、文字列が空でないかを確認してから処理しましょう。
5-5. nullの文字列にSubstringを使った場合
nullの文字列に対してSubstringを呼び出すと、NullReferenceExceptionが発生します。
C#string text = null;
string result = text.Substring(0, 1);
Substringはstringインスタンスのメソッドなので、対象の変数がnullの場合は呼び出せません。
安全に処理するには、nullチェックが必要です。
C#string text = null;
if (text != null)
{
string result = text.Substring(0, 1);
Console.WriteLine(result);
}
else
{
Console.WriteLine("文字列がnullです。");
}
または、string.IsNullOrEmptyを使うと、nullと空文字をまとめて判定できます。
C#string text = null;
if (!string.IsNullOrEmpty(text))
{
string result = text.Substring(0, 1);
Console.WriteLine(result);
}
else
{
Console.WriteLine("文字列がnullまたは空です。");
}
6. Substringのエラー対策と安全な書き方
実務でSubstringを使う場合は、想定外の文字列が渡されることを考慮する必要があります。
ここでは、エラーを防ぐための安全な書き方を紹介します。
6-1. Lengthで文字列の長さを確認する
もっとも基本的な対策は、Lengthで文字列の長さを確認することです。
C#string text = "abc";
if (text.Length >= 2)
{
string result = text.Substring(0, 2);
Console.WriteLine(result);
}
else
{
Console.WriteLine("文字列が短すぎます。");
}
C#ab
指定した文字数を切り出す前に、必要な長さがあるか確認しましょう。
末尾3文字を取得する場合は、次のように書けます。
C#string text = "abcdef";
if (text.Length >= 3)
{
string result = text.Substring(text.Length - 3);
Console.WriteLine(result);
}
else
{
Console.WriteLine("文字列が3文字未満です。");
}
C#def
6-2. string.IsNullOrEmptyでnull・空文字を判定する
nullや空文字をまとめてチェックしたい場合は、string.IsNullOrEmptyを使います。
C#string text = "";
if (!string.IsNullOrEmpty(text))
{
string result = text.Substring(0, 1);
Console.WriteLine(result);
}
else
{
Console.WriteLine("文字列がnullまたは空です。");
}
空白だけの文字列も無効としたい場合は、string.IsNullOrWhiteSpaceを使います。
C#string text = " ";
if (!string.IsNullOrWhiteSpace(text))
{
string result = text.Substring(0, 1);
Console.WriteLine(result);
}
else
{
Console.WriteLine("文字列がnull、空、または空白のみです。");
}
ユーザー入力や外部ファイル、APIレスポンスなどを処理する場合は、nullや空文字が混ざる可能性があるため、事前チェックを入れるのが安全です。
6-3. IndexOfの戻り値が-1か確認する
IndexOfとSubstringを組み合わせる場合は、IndexOfの戻り値が-1でないか確認しましょう。
C#string email = "user@example.com";
int atIndex = email.IndexOf("@");
if (atIndex >= 0)
{
string domain = email.Substring(atIndex + 1);
Console.WriteLine(domain);
}
else
{
Console.WriteLine("@が見つかりません。");
}
IndexOfは、指定した文字や文字列が見つからない場合に-1を返します。
次のようにチェックせずに使うと危険です。
C#string email = "userexample.com";
int atIndex = email.IndexOf("@");
string domain = email.Substring(atIndex + 1);
この場合、atIndexは-1です。
atIndex + 1は0になるため、例外にはならない場合もありますが、意図しない結果になります。
前半部分を切り出す場合は、より直接的にエラーになることがあります。
C#string email = "userexample.com";
int atIndex = email.IndexOf("@");
string userName = email.Substring(0, atIndex);
lengthに-1が指定されるため、ArgumentOutOfRangeExceptionになります。
6-4. 範囲外にならないようにMath.Minを使う
「最大でn文字まで取得したい」という場合は、Math.Minを使うと安全です。
C#string text = "abc";
int length = Math.Min(5, text.Length);
string result = text.Substring(0, length);
Console.WriteLine(result);
実行結果は次のとおりです。
C#abc
本来は5文字取得したいけれど、文字列が3文字しかない場合は、取得できる範囲だけ切り出します。
先頭10文字までを表示するような処理にも使えます。
C#string title = "C# Substringの使い方を完全解説";
int maxLength = 10;
string shortTitle = title.Substring(0, Math.Min(maxLength, title.Length));
Console.WriteLine(shortTitle);
ただし、textがnullの場合はLengthを参照した時点でエラーになります。Math.Minを使う前に、必要に応じてnullチェックも行いましょう。
6-5. 安全に切り出す共通メソッドを作る
Substringの範囲チェックを毎回書くのが面倒な場合は、安全に切り出す共通メソッドを作ると便利です。
C#static string SafeSubstring(string text, int startIndex, int length)
{
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
if (startIndex < 0)
{
return string.Empty;
}
if (startIndex >= text.Length)
{
return string.Empty;
}
int safeLength = Math.Min(length, text.Length - startIndex);
if (safeLength < 0)
{
return string.Empty;
}
return text.Substring(startIndex, safeLength);
}
使用例は次のとおりです。
C#string text = "abcdef";
Console.WriteLine(SafeSubstring(text, 2, 3));
Console.WriteLine(SafeSubstring(text, 4, 10));
Console.WriteLine(SafeSubstring(text, 10, 2));
実行結果は次のようになります。
C#cde
ef
範囲外の場合は例外を投げず、空文字を返すようにしています。
ただし、エラーを隠してしまうと不具合に気づきにくくなる場合もあります。業務ロジックによっては、空文字を返すのではなく、明確に例外を投げたり、エラーメッセージを返したりする設計も検討しましょう。
7. Substringと他の文字列操作メソッドの使い分け
C#には、Substring以外にも文字列操作のためのメソッドが多く用意されています。
ここでは、代表的なメソッドとの違いを見ていきます。
7-1. Splitとの違い
Splitは、指定した区切り文字で文字列を分割するメソッドです。
C#string text = "apple,banana,orange";
string[] fruits = text.Split(',');
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
実行結果は次のとおりです。
C#apple
banana
orange
Substringは位置や文字数で切り出すメソッドです。
一方、Splitはカンマやタブ、スラッシュなどの区切り文字で分割するときに向いています。
固定位置で取り出すならSubstring、区切り文字で分けるならSplitと考えるとわかりやすいです。
7-2. Replaceとの違い
Replaceは、文字列の一部を別の文字列に置き換えるメソッドです。
C#string text = "Hello Java";
string result = text.Replace("Java", "C#");
Console.WriteLine(result);
C#Hello C#
Substringは文字列を切り出すメソッドです。
Replaceは文字列を置き換えるメソッドです。
たとえば、文字列から一部を取得したい場合はSubstringを使います。
C#string text = "Hello C#";
string result = text.Substring(6);
Console.WriteLine(result);
C#C#
文字列内の特定の語句を変更したい場合はReplaceを使います。
C#string text = "Hello C#";
string result = text.Replace("C#", "World");
Console.WriteLine(result);
C#Hello World
7-3. Removeとの違い
Removeは、文字列の一部を削除するメソッドです。
C#string text = "HelloWorld";
string result = text.Remove(5);
Console.WriteLine(result);
実行結果は次のとおりです。
C#Hello
Remove(5)は、インデックス5以降を削除します。
指定した範囲だけを削除することもできます。
C#string text = "HelloWorld";
string result = text.Remove(5, 5);
Console.WriteLine(result);
C#Hello
Substringは必要な部分を取り出すメソッド、Removeは不要な部分を削除した結果を返すメソッドです。
同じ結果を得られる場合もあります。
C#string text = "HelloWorld";
string result1 = text.Substring(0, 5);
string result2 = text.Remove(5);
Console.WriteLine(result1);
Console.WriteLine(result2);
C#Hello
Hello
先頭から指定文字数を残したい場合は、Substring(0, n)でもRemove(n)でも表現できます。読みやすさを考えて選びましょう。
7-4. IndexOf・LastIndexOfとの組み合わせ
IndexOfは、指定した文字や文字列が最初に現れる位置を返します。
C#string text = "abc-def-ghi";
int index = text.IndexOf("-");
Console.WriteLine(index);
C#3
LastIndexOfは、指定した文字や文字列が最後に現れる位置を返します。
C#string text = "abc-def-ghi";
int index = text.LastIndexOf("-");
Console.WriteLine(index);
C#7
これらをSubstringと組み合わせると、特定の文字の前後を切り出せます。
C#string text = "abc-def-ghi";
int firstHyphen = text.IndexOf("-");
string before = text.Substring(0, firstHyphen);
string after = text.Substring(firstHyphen + 1);
Console.WriteLine(before);
Console.WriteLine(after);
C#abc
def-ghi
最後のハイフン以降を切り出す例です。
C#string text = "abc-def-ghi";
int lastHyphen = text.LastIndexOf("-");
string lastPart = text.Substring(lastHyphen + 1);
Console.WriteLine(lastPart);
C#ghi
Substring単体では「特定の文字の位置」を探せないため、IndexOfやLastIndexOfと組み合わせる場面はとても多いです。
7-5. ContainsやStartsWithと併用するケース
Containsは、指定した文字列が含まれているかを判定します。
C#string text = "user@example.com";
if (text.Contains("@"))
{
Console.WriteLine("メールアドレスのような形式です。");
}
StartsWithは、文字列が指定した文字列で始まるかを判定します。
C#string url = "https://example.com";
if (url.StartsWith("https://"))
{
Console.WriteLine("HTTPSのURLです。");
}
Substringと組み合わせることで、条件を満たした場合だけ切り出す処理を書けます。
C#string url = "https://example.com";
if (url.StartsWith("https://"))
{
string domain = url.Substring("https://".Length);
Console.WriteLine(domain);
}
C#example.com
ただし、Containsで存在確認をしたあとに位置を使いたい場合は、結局IndexOfが必要になることが多いです。
C#string text = "key=value";
int equalIndex = text.IndexOf("=");
if (equalIndex >= 0)
{
string key = text.Substring(0, equalIndex);
string value = text.Substring(equalIndex + 1);
Console.WriteLine(key);
Console.WriteLine(value);
}
C#key
value
存在確認だけならContains、位置を使って切り出すならIndexOfを使うとよいでしょう。
8. C# 8.0以降の範囲演算子との違い
C# 8.0以降では、範囲演算子を使って文字列を切り出すこともできます。
範囲演算子を使うと、Substringより短く直感的に書ける場合があります。
8-1. Range構文で文字列を切り出す方法
C# 8.0以降では、..を使った範囲指定ができます。
C#string text = "HelloWorld";
string result = text[0..5];
Console.WriteLine(result);
実行結果は次のとおりです。
C#Hello
text[0..5]は、インデックス0からインデックス5の手前までを切り出します。
末尾まで切り出す場合は、終了位置を省略できます。
C#string text = "HelloWorld";
string result = text[5..];
Console.WriteLine(result);
C#World
先頭から指定位置の手前までを切り出す場合は、開始位置を省略できます。
C#string text = "HelloWorld";
string result = text[..5];
Console.WriteLine(result);
C#Hello
末尾から数える場合は、^を使います。
C#string text = "HelloWorld";
string result = text[^5..];
Console.WriteLine(result);
C#World
^5は、末尾から5番目の位置を意味します。
8-2. Substringと範囲演算子の書き方比較
Substringと範囲演算子は、同じような処理を別の書き方で表現できます。
先頭5文字を取り出す場合です。
C#string text = "HelloWorld";
string result1 = text.Substring(0, 5);
string result2 = text[..5];
Console.WriteLine(result1);
Console.WriteLine(result2);
C#Hello
Hello
インデックス5から末尾までを取り出す場合です。
C#string text = "HelloWorld";
string result1 = text.Substring(5);
string result2 = text[5..];
Console.WriteLine(result1);
Console.WriteLine(result2);
C#World
World
末尾5文字を取り出す場合です。
C#string text = "HelloWorld";
string result1 = text.Substring(text.Length - 5);
string result2 = text[^5..];
Console.WriteLine(result1);
Console.WriteLine(result2);
C#World
World
範囲演算子を使うと、末尾からの切り出しが簡潔に書けます。
8-3. どちらを使うべきか
Substringと範囲演算子のどちらを使うべきかは、プロジェクトのC#バージョンやチームのコーディング方針によります。
古いC#バージョンでも使える書き方にしたい場合は、Substringが無難です。
C#string result = text.Substring(0, 5);
C# 8.0以降を使っていて、範囲指定のほうが読みやすい場合は、範囲演算子を使うとよいでしょう。
C#string result = text[..5];
特に、末尾からの切り出しは範囲演算子のほうが読みやすくなります。
C#string last5 = text[^5..];
一方で、Substring(startIndex, length)は「開始位置」と「文字数」を明確に書けるため、固定長データの切り出しでは読みやすい場合があります。
C#string year = date.Substring(0, 4);
string month = date.Substring(4, 2);
string day = date.Substring(6, 2);
範囲演算子は「開始位置」と「終了位置」を指定する書き方です。
C#string year = date[0..4];
string month = date[4..6];
string day = date[6..8];
どちらが読みやすいかはケースによって異なります。
8-4. 古いC#バージョンでの注意点
範囲演算子はC# 8.0以降の機能です。
古いプロジェクトでは、次のようなコードが使えない場合があります。
C#string text = "HelloWorld";
string result = text[..5];
その場合は、Substringを使います。
C#string text = "HelloWorld";
string result = text.Substring(0, 5);
また、チーム内で古い開発環境を使っている場合や、複数のプロジェクトで共通ライブラリを使っている場合は、範囲演算子を使う前に対応状況を確認しておくと安心です。
9. パフォーマンス面で知っておきたいSubstringの注意点
Substringは手軽に使えるメソッドですが、大量の文字列処理を行う場合はパフォーマンスにも注意が必要です。
9-1. Substringは新しい文字列を生成する
Substringは、切り出した結果として文字列を返します。
C#string text = "HelloWorld";
string result = text.Substring(0, 5);
このresultは、元の文字列とは別の文字列として扱われます。
C#のstringは不変なので、文字列操作を行うたびに新しい文字列が作られることがあります。
通常のアプリケーションでは大きな問題になることは少ないですが、大量のデータを処理する場合は、不要な文字列生成が積み重なってメモリ使用量や処理速度に影響することがあります。
9-2. 大量処理でSubstringを多用する場合の注意
たとえば、ログファイルを1行ずつ読み込み、大量のSubstringを行うような処理では注意が必要です。
C#foreach (string line in lines)
{
string date = line.Substring(0, 8);
string level = line.Substring(9, 5);
string message = line.Substring(15);
}
データ件数が少なければ問題になりにくいですが、数百万行のような大量処理では、文字列の生成回数が多くなります。
このような場合は、以下のような工夫を検討します。
C#if (line.Length >= 15)
{
string date = line.Substring(0, 8);
string level = line.Substring(9, 5);
string message = line.Substring(15);
}
まずは範囲外エラーを防ぐことが重要です。
さらにパフォーマンスが問題になる場合は、ReadOnlySpan<char>などの利用も検討できます。
9-3. ReadOnlySpan<char>を使う選択肢
ReadOnlySpan<char>を使うと、文字列の一部をコピーせずに参照できます。
C#string text = "HelloWorld";
ReadOnlySpan<char> span = text.AsSpan();
ReadOnlySpan<char> firstPart = span.Slice(0, 5);
Console.WriteLine(firstPart.ToString());
実行結果は次のとおりです。
C#Hello
ただし、ToString()を呼び出すと、その時点で新しい文字列が生成されます。
ReadOnlySpan<char>は、文字列を最終的に生成せずに解析・比較・変換するような場面で効果を発揮します。
たとえば、文字列の一部を数値に変換する場合です。
C#string text = "20260606";
ReadOnlySpan<char> yearSpan = text.AsSpan(0, 4);
int year = int.Parse(yearSpan);
Console.WriteLine(year);
C#2026
ただし、ReadOnlySpan<char>はSubstringよりもやや高度な機能です。通常の文字列処理では、まずSubstringで読みやすく書き、必要に応じて最適化を考えるのが現実的です。
9-4. 可読性と性能のバランス
パフォーマンスを意識することは大切ですが、すべてのSubstringを無理に避ける必要はありません。
多くの業務アプリケーションでは、Substringのわかりやすさがメリットになります。
C#string year = date.Substring(0, 4);
string month = date.Substring(4, 2);
string day = date.Substring(6, 2);
このコードは、何を取り出しているのかが明確です。
一方、極端に大量の文字列を処理する場合や、メモリ使用量を厳しく抑えたい場合は、ReadOnlySpan<char>や専用のパーサーを検討する価値があります。
まずは正しく読みやすいコードを書き、実際にパフォーマンス上の問題が出た場合に最適化するのが基本です。
10. Substringでよくある疑問
最後に、C#のSubstringでよくある疑問をまとめます。
10-1. 最後の1文字を取得するには?
最後の1文字を取得するには、Length - 1を開始位置に指定します。
C#string text = "Hello";
string last = text.Substring(text.Length - 1);
Console.WriteLine(last);
実行結果は次のとおりです。
C#o
ただし、空文字の場合はエラーになります。
安全に書くなら、次のようにチェックします。
C#string text = "Hello";
if (!string.IsNullOrEmpty(text))
{
string last = text.Substring(text.Length - 1);
Console.WriteLine(last);
}
C# 8.0以降であれば、範囲演算子を使って次のようにも書けます。
C#string text = "Hello";
string last = text[^1..];
Console.WriteLine(last);
C#o
1文字のcharとして取得したい場合は、インデックスアクセスを使えます。
C#string text = "Hello";
char last = text[text.Length - 1];
Console.WriteLine(last);
C#o
Substringは文字列を返し、インデックスアクセスはcharを返す点が違います。
10-2. 先頭のn文字だけ取得するには?
先頭のn文字を取得するには、Substring(0, n)を使います。
C#string text = "HelloWorld";
string result = text.Substring(0, 5);
Console.WriteLine(result);
C#Hello
ただし、文字列がn文字未満の場合はエラーになります。
安全に書くなら、Math.Minを使います。
C#string text = "abc";
int n = 5;
string result = text.Substring(0, Math.Min(n, text.Length));
Console.WriteLine(result);
C#abc
nullの可能性がある場合は、さらにnullチェックも必要です。
C#string text = null;
int n = 5;
string result = string.IsNullOrEmpty(text)
? string.Empty
: text.Substring(0, Math.Min(n, text.Length));
Console.WriteLine(result);
10-3. 指定文字の前後を切り出すには?
指定文字の前後を切り出すには、IndexOfで位置を探してからSubstringを使います。
C#string text = "key=value";
int index = text.IndexOf("=");
if (index >= 0)
{
string before = text.Substring(0, index);
string after = text.Substring(index + 1);
Console.WriteLine(before);
Console.WriteLine(after);
}
実行結果は次のとおりです。
C#key
value
最後に出てくる指定文字を基準にしたい場合は、LastIndexOfを使います。
C#string text = "folder/subfolder/file.txt";
int index = text.LastIndexOf("/");
if (index >= 0)
{
string fileName = text.Substring(index + 1);
Console.WriteLine(fileName);
}
C#file.txt
指定文字が存在しない可能性がある場合は、必ずindex >= 0を確認しましょう。
10-4. 空文字が返るのはどんな場合?
Substringで空文字が返る代表的なケースは、startIndexが文字列の長さと同じ場合です。
C#string text = "abc";
string result = text.Substring(3);
Console.WriteLine($"'{result}'");
実行結果は次のとおりです。
C#''
また、lengthに0を指定した場合も空文字が返ります。
C#string text = "abc";
string result = text.Substring(1, 0);
Console.WriteLine($"'{result}'");
C#''
これはエラーではありません。
ただし、startIndexが文字列の長さを超える場合や、startIndex + lengthが文字列の長さを超える場合はエラーになります。
C#string text = "abc";
// エラー
string result = text.Substring(4);
空文字が返るケースと例外が発生するケースを区別しておきましょう。
10-5. Substringで元の文字列は変わる?
Substringを使っても、元の文字列は変わりません。
C#string text = "HelloWorld";
string result = text.Substring(0, 5);
Console.WriteLine(text);
Console.WriteLine(result);
実行結果は次のとおりです。
C#HelloWorld
Hello
textはHelloWorldのままです。
切り出した結果を使いたい場合は、戻り値を変数に代入する必要があります。
C#string text = "HelloWorld";
text.Substring(0, 5);
Console.WriteLine(text);
このコードでは、Substringの結果を受け取っていないため、表示されるのは元の文字列です。
C#HelloWorld
結果を使うには次のように書きます。
C#string text = "HelloWorld";
text = text.Substring(0, 5);
Console.WriteLine(text);
C#Hello
この場合、変数textに新しい文字列を再代入しています。元の文字列そのものを変更しているわけではありません。
まとめ
C#のSubstringは、文字列の一部を切り出すための基本的なメソッドです。
指定した位置から末尾まで切り出す場合は、次のように書きます。
C#string result = text.Substring(startIndex);
指定した位置から指定文字数だけ切り出す場合は、次のように書きます。
C#string result = text.Substring(startIndex, length);
startIndexは切り出し開始位置、lengthは切り出す文字数です。
C#の文字列インデックスは0から始まるため、先頭の文字はインデックス0になります。
C#string text = "ABCDE";
string result = text.Substring(0, 2);
Console.WriteLine(result);
C#AB
Substringを使うときに特に注意したいのは、範囲外エラーです。
startIndexが文字列の長さを超えている場合や、startIndex + lengthが文字列の長さを超えている場合、負の数を指定した場合などは、ArgumentOutOfRangeExceptionが発生します。
また、対象の文字列がnullの場合はNullReferenceExceptionが発生します。
安全に使うには、次のようなチェックが重要です。
C#if (!string.IsNullOrEmpty(text) && text.Length >= 5)
{
string result = text.Substring(0, 5);
Console.WriteLine(result);
}
固定長のコードやID、日付文字列、ファイル名、URL、メールアドレスなど、位置が決まっている文字列から一部を取り出す処理では、Substringが便利です。
一方、区切り文字で分割したい場合はSplit、文字列を置き換えたい場合はReplace、一部を削除したい場合はRemoveを使うなど、目的に応じてメソッドを使い分けましょう。
C# 8.0以降では、範囲演算子を使って次のように書くこともできます。
C#string text = "HelloWorld";
string first = text[..5];
string last = text[^5..];
Console.WriteLine(first);
Console.WriteLine(last);
C#Hello
World
Substringはシンプルで使いやすい反面、範囲指定を間違えると例外が発生しやすいメソッドです。
Length、string.IsNullOrEmpty、IndexOfの戻り値チェック、Math.Minなどを組み合わせて、安全で読みやすいコードを書くことが大切です。

