閉じる

C#で文字列を置換・切り出し!ReplaceとSubstring徹底解説

C#を用いたアプリケーション開発において、文字列操作は避けては通れない基本的な処理です。

ユーザーからの入力値を整形したり、ログファイルから特定の情報を抽出したりと、その用途は多岐にわたります。

中でも「置換(Replace)」と「切り出し(Substring)」は、頻繁に利用される非常に重要なメソッドです。

本記事では、これら2つのメソッドの基本的な使い方から、最新のC#で推奨される効率的な記述方法、さらにはパフォーマンスを意識した実践的なテクニックまでを徹底的に解説します。

文字列操作の基本:イミュータブルな性質を知る

C#において文字列(string)を扱う際、まず理解しておくべき重要な概念が「イミュータブル(不変性)」です。

これは一度作成された文字列オブジェクトの内容を後から変更することはできないという性質を指します。

C#のReplaceSubstringといったメソッドは、元の文字列を書き換えるのではなく、処理結果として新しい文字列を生成して返します。

したがって、メソッドを呼び出しただけでは元の変数の値は変わりません。

必ず戻り値を変数に代入し直すか、そのまま別の処理に渡す必要があります。

Replaceメソッドによる文字列の置換

Replaceメソッドは、文字列内にある特定の文字や文字列を探し出し、それを別の内容に置き換えるために使用されます。

Replaceメソッドの基本的な使い方

最もシンプルな使い方は、第一引数に「置換前の文字列」、第二引数に「置換後の文字列」を指定する方法です。

C#
using System;

class Program
{
    static void Main()
    {
        string original = "Apple, Orange, Apple, Grape";
        
        // "Apple" を "Banana" に置換する
        // 全ての該当箇所が置換されます
        string result = original.Replace("Apple", "Banana");

        Console.WriteLine("元の文字列: " + original);
        Console.WriteLine("置換後: " + result);
    }
}
実行結果
元の文字列: Apple, Orange, Apple, Grape
置換後: Banana, Orange, Banana, Grape

このコードからわかる通り、Replaceメソッドは見つかったすべての対象文字列を置換します。

また、引数にはstring(文字列)だけでなく、char(単一文字)を渡すことも可能です。

大文字・小文字を区別しない置換

デフォルトのReplaceメソッドは、大文字と小文字を厳密に区別します。

しかし、実務ではこれらを区別せずに置換したいケースも多いでしょう。

その場合は、StringComparison列挙型を指定できるオーバーロードを使用します。

C#
using System;

class Program
{
    static void Main()
    {
        string text = "C# is powerful. c# is fun.";

        // 大文字小文字を無視して置換
        string result = text.Replace("c#", "C-Sharp", StringComparison.OrdinalIgnoreCase);

        Console.WriteLine(result);
    }
}
実行結果
C-Sharp is powerful. C-Sharp is fun.

StringComparison.OrdinalIgnoreCaseを指定することで、大文字の「C#」と小文字の「c#」の両方が置換対象となります。

これにより、ユーザー入力の揺れを吸収する柔軟なプログラムが記述できます。

連続した置換とメソッドチェーン

複数の異なる文字列を一度に置換したい場合、メソッドをドットでつなぐ「メソッドチェーン」を利用するとコードがスッキリします。

C#
using System;

class Program
{
    static void Main()
    {
        string text = "【重要】価格は100円(税込)です。";

        // 複数の記号や文字を一度に除去・置換する
        string cleanText = text.Replace("【", "")
                               .Replace("】", ":")
                               .Replace("(", " ")
                               .Replace(")", "");

        Console.WriteLine(cleanText);
    }
}
実行結果
重要:価格は100円 税込 です。

ただし、メソッドチェーンを多用しすぎると、その回数分だけ新しい文字列オブジェクトがメモリ上に生成されるため、大量のデータを処理する際はパフォーマンスに注意が必要です。

改行コードの置換 (ReplaceLineEndings)

近年の.NET(C#)では、OSごとに異なる改行コード(\r\n\n)を一括で特定の形式に揃えるReplaceLineEndingsメソッドが導入されました。

C#
string text = "Line1\r\nLine2\nLine3";
// すべての改行を環境に合わせた形式に統一
string unifiedText = text.ReplaceLineEndings();

これを使えば、面倒な正規表現や複数のReplaceを使わずに、マルチプラットフォーム対応のコードが簡単に書けます。

Substringメソッドによる文字列の切り出し

Substringメソッドは、文字列の指定した位置から特定の長さ分だけを抽出するために使用します。

Substringメソッドの基本的な使い方

Substringには主に2つの引数の指定方法があります。

指定方法説明
Substring(開始インデックス)開始位置から最後までを切り出す
Substring(開始インデックス, 長さ)開始位置から指定した文字数分だけ切り出す
C#
using System;

class Program
{
    static void Main()
    {
        string target = "Microsoft .NET C#";

        // 10番目の文字から最後まで
        string sub1 = target.Substring(10); 
        
        // 0番目の文字から9文字分
        string sub2 = target.Substring(0, 9);

        Console.WriteLine("抽出1: " + sub1);
        Console.WriteLine("抽出2: " + sub2);
    }
}
実行結果
抽出1: .NET C#
抽出2: Microsoft

C#のインデックスは0から始まる点に注意してください。

1文字目を取得したい場合は、インデックスに0を指定します。

IndexOfとSubstringの組み合わせ

「特定の文字が現れるまでの部分を抽出したい」というように、切り出す位置が動的に変わる場合は、IndexOfメソッドで位置を検索してからSubstringを実行します。

C#
using System;

class Program
{
    static void Main()
    {
        string email = "user_name@example.com";

        // '@' の位置を探す
        int atIndex = email.IndexOf('@');

        if (atIndex != -1)
        {
            // 先頭から '@' の手前までを切り出す
            string userName = email.Substring(0, atIndex);
            Console.WriteLine("ユーザー名: " + userName);
        }
    }
}
実行結果
ユーザー名: user_name

IndexOfが見つからなかった場合は-1を返すため、必ずチェックを行うのがバグを防ぐコツです。

モダンC#による新しい切り出し手法:範囲演算子 (Range)

C# 8.0以降では、Substringの代わりにより直感的に記述できる「範囲演算子(..)」と「末尾からのインデックス演算子(^)」が導入されました。

C#
using System;

class Program
{
    static void Main()
    {
        string text = "ABCDEFG";

        // Substring(1, 3) と同等 (インデックス1から4の手前まで)
        string range1 = text[1..4]; 

        // 末尾から3文字分を取得
        string range2 = text[^3..];

        Console.WriteLine("range1: " + range1);
        Console.WriteLine("range2: " + range2);
    }
}
実行結果
range1: BCD
range2: EFG

この記法を使うと、特に「末尾から数文字」といった指定が非常に簡潔になります。

Substringではtext.Length - 3のような計算が必要でしたが、^3と書くだけで済むのは大きなメリットです。

正規表現による高度な置換 (Regex.Replace)

単純な文字列一致ではなく、「数字だけを置換したい」「特定のパターンに一致する場合だけ置換したい」といった複雑な要件には、System.Text.RegularExpressions名前空間のRegex.Replaceを使用します。

C#
using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        string input = "Order ID: 12345, Price: 500 yen";

        // 半角数字 (\d+) をすべて "xxx" に置換
        string masked = Regex.Replace(input, @"\d+", "xxx");

        Console.WriteLine(masked);
    }
}
実行結果
Order ID: xxx, Price: xxx yen

正規表現は強力ですが、通常のReplaceよりも処理コストが高いため、単純な置換で済む場合は標準のメソッドを使用するのがパフォーマンス上の定石です。

パフォーマンスを意識した文字列操作

大量の置換を繰り返す場合、前述した「文字列の不変性」がネックとなり、メモリ消費量が増大して動作が重くなることがあります。

StringBuilderの活用

何百回、何千回と置換を繰り返すようなループ処理の中では、StringBuilderクラスを使用しましょう。

StringBuilderは内部でバッファを持ち、新しいオブジェクトを作らずにその場で文字列を書き換えることができるため、非常に高速です。

C#
using System;
using System.Text;

class Program
{
    static void Main()
    {
        StringBuilder sb = new StringBuilder("Initial text. ");

        for (int i = 0; i < 1000; i++)
        {
            sb.Append("Data").Replace("Data", "Value");
        }

        string finalResult = sb.ToString();
        Console.WriteLine("処理完了");
    }
}

ReadOnlySpan<char> によるメモリ節約

C#の比較的新しい機能として、ReadOnlySpan<char>を用いた切り出しがあります。

Substringは新しい文字列を生成(コピー)しますが、Spanを使用すると「元の文字列の特定の範囲を指し示す」だけなので、メモリのコピーが発生しません。

C#
using System;

class Program
{
    static void Main()
    {
        string largeText = "This is a very large string...";

        // メモリコピーを発生させずに一部を参照
        ReadOnlySpan<char> span = largeText.AsSpan(10, 5);

        // 数値変換など、Spanをサポートするメソッドに直接渡せる
        // int.Parse(largeText.Substring(10, 5)) よりも効率的
        Console.WriteLine(span.ToString()); 
    }
}

高頻度で呼ばれる解析処理(パーサーなど)では、このSpanを活用することでアプリケーション全体のパフォーマンスが劇的に向上します。

よくあるエラーと対処法

文字列操作で最も頻繁に遭遇する例外がArgumentOutOfRangeExceptionです。

境界値チェックの重要性

Substringにおいて、文字列の長さを超えるインデックスや、合計が文字列長を超える「長さ」を指定するとエラーになります。

C#
string s = "Hi";
// 文字列は2文字しかないのに3文字目から取得しようとしてエラー
// string error = s.Substring(3);

これを防ぐには、事前にLengthプロパティで長さを確認するか、前述のIndexOfの結果が-1でないことを確認する習慣をつけましょう。

また、nullに対してメソッドを呼ぶとNullReferenceExceptionが発生するため、string.IsNullOrEmpty()などでのチェックも欠かせません。

まとめ

C#の文字列置換と切り出しは、基本でありながら非常に奥が深いテーマです。

単にReplaceSubstringを使うだけでなく、「大文字小文字の区別をどうするか」「パフォーマンスへの影響はどうか」「モダンな記法を使えないか」といった視点を持つことで、より高品質なコードを書くことができます。

最後に、今回紹介した手法の使い分けをまとめます。

  • 単純な置き換え:Replace
  • 複雑なパターン置換:Regex.Replace
  • 大量の連続置換:StringBuilder
  • 決まった位置の抽出:Substring または 範囲演算子 [..]
  • メモリ効率を極める抽出:ReadOnlySpan<char>

これらの道具を適切に使い分け、効率的で読みやすい文字列操作を実現しましょう。

基本的なメソッドをマスターすることは、C#エンジニアとしてのステップアップに直結します。

基本操作

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!