閉じる

【C#】Splitで空白を削除!RemoveEmptyEntriesの使い方

C#で文字列を処理する際、特定の区切り文字で分割するString.Splitメソッドは欠かせない機能です。

しかし、入力データに連続した区切り文字や末尾の空白が含まれていると、分割後の配列の中に「空の文字列」が混入してしまうことがあります。

この空の要素は、その後のループ処理やデータ変換において予期せぬエラーを引き起こす原因となり得ます。

本記事では、これらの不要な空要素を効率的に取り除くためのStringSplitOptions.RemoveEmptyEntriesの活用方法を中心に、実践的な文字列分割テクニックを詳しく解説します。

Splitメソッドの基本と空の要素が発生する仕組み

C#のstring.Splitメソッドは、指定した文字や文字列を基準に元のテキストを切り分け、配列として返す機能を持っています。

まずは、なぜ「空の要素」が発生してしまうのか、その仕組みを正しく理解しましょう。

なぜ空の文字列が含まれるのか

分割対象の文字列の中に区切り文字が連続して存在する場合、プログラムは「区切り文字と区切り文字の間には何もない文字列が存在する」と解釈します。

例えば、CSVデータなどでカンマが2つ続いている場合、標準のSplitを実行すると、その間にあるはずのデータが「空文字列("")」として抽出されます。

以下のサンプルコードで、デフォルトの挙動を確認してみましょう。

C#
using System;

class Program
{
    static void Main()
    {
        // カンマが連続している文字列
        string data = "C#,Java,,Python,,PHP";

        // 通常のSplitメソッドを実行
        string[] languages = data.Split(',');

        Console.WriteLine($"要素数: {languages.Length}");
        
        // 各要素を表示(空文字がわかりやすいように[]で囲む)
        foreach (string lang in languages)
        {
            Console.WriteLine($"[{lang}]");
        }
    }
}
実行結果
要素数: 5
[C#]
[Java] []
[Python] []
[PHP]

このように、デフォルトの設定では連続したカンマの間に存在する空のデータも1つの要素としてカウントされてしまいます。

これをそのまま計算やデータベース登録に使用すると、空文字チェックを個別に行う必要があり、コードが複雑化してしまいます。

デフォルト挙動のメリットとデメリット

デフォルトの挙動は一見不便に見えますが、必ずしも悪いわけではありません。

例えば、Excelのようなスプレッドシート形式のデータを扱う場合、「空であること自体が情報」であるケースがあります(未入力のセルなど)。

そのような場合は、空の要素を保持しておく必要があります。

一方で、単にキーワードを抽出したい場合や、ユーザー入力を整理したい場合には、これらの空要素はノイズでしかありません。

そこで登場するのがStringSplitOptionsです。

RemoveEmptyEntriesによる空要素の自動削除

空の要素を配列に含めたくない場合は、Splitメソッドの第2引数にStringSplitOptions.RemoveEmptyEntriesを指定します。

これにより、分割処理の過程で空文字列と判定された要素が自動的に破棄されます。

RemoveEmptyEntriesの基本的な使い方

このオプションを使用するには、Splitメソッドのオーバーロードを利用します。

最も一般的な形式は、区切り文字とオプションをセットで渡す方法です。

C#
using System;

class Program
{
    static void Main()
    {
        string input = "りんご,,バナナ,,,スイカ";

        // 第2引数に StringSplitOptions.RemoveEmptyEntries を指定
        string[] result = input.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

        Console.WriteLine($"クリーンアップ後の要素数: {result.Length}");

        foreach (string item in result)
        {
            Console.WriteLine($"要素: {item}");
        }
    }
}
実行結果
クリーンアップ後の要素数: 3
要素: りんご
要素: バナナ
要素: スイカ

上記のコードでは、複数のカンマが重なっている箇所があっても、有効な文字列だけが抽出されていることがわかります。

配列のサイズも、見つかった有効な要素の数に合わせて適切に確保されます。

StringSplitOptions列挙型の種類

StringSplitOptionsには、主に以下の2つの値が定義されています。

定数名説明
None既定の動作。空の配列要素も戻り値に含める。
RemoveEmptyEntries空の文字列を含む配列要素を戻り値に含めない。
TrimEntries(.NET 5以降)各要素の先頭と末尾にある空白文字を削除する。

最近の.NETバージョンでは、後述するTrimEntriesと組み合わせて使用することで、より強力な文字列クリーニングが可能になっています。

空白文字を含んだ分割とTrimEntriesの活用

実務でよく遭遇する問題として、「区切り文字の周りにスペースが入っている」というパターンがあります。

例えば、"Apple, Orange , Banana"のような文字列をカンマで分割すると、" Orange "のように前後にスペースが残った状態で配列に格納されてしまいます。

.NET 5以降で利用可能なTrimEntries

従来のC#では、Splitした後にSelect(s => s.Trim())などのLINQを使って空白を除去していましたが、.NET 5以降ではStringSplitOptions.TrimEntriesが追加されました。

これをRemoveEmptyEntriesと組み合わせることで、「空白を削り、もしその結果空になったら削除する」という高度な処理が1行で書けます。

C#
using System;

class Program
{
    static void Main()
    {
        // スペースや空要素が混じった複雑な文字列
        string rawData = "  C#  , , Java , Python ,  ";

        // ビット演算( | )でオプションを複数指定
        string[] cleanData = rawData.Split(
            new char[] { ',' }, 
            StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
        );

        Console.WriteLine($"処理後の要素数: {cleanData.Length}");

        foreach (string s in cleanData)
        {
            Console.WriteLine($"[{s}]");
        }
    }
}
実行結果
処理後の要素数: 3
[C#]
[Java]
[Python]

オプションの組み合わせによる挙動の変化

StringSplitOptions.TrimEntriesを単体で使用した場合と、RemoveEmptyEntriesを併用した場合では結果が異なります。

TrimEntriesのみ

各要素のトリミングは行われますが、もともと「スペースのみ」だった箇所は「空文字列」として配列に残ります。

RemoveEmptyEntries | TrimEntries

トリミングを行った結果、空になった要素もすべて削除されます。

一般的に、データの整形を目的とするならば2つのオプションを組み合わせて使用するのが最も安全で確実な方法です。

複数の区切り文字を指定して分割する

実際の業務では、区切り文字が1種類とは限りません。

例えば、カンマ、セミコロン、スペースが混在しているデータを一括で分割したいケースがあります。

文字配列(char[])による複数指定

Splitメソッドの引数には、区切り文字の配列を渡すことができます。

C#
using System;

class Program
{
    static void Main()
    {
        // 記号が混在する文字列
        string multiDelim = "Red;Blue, Green/Yellow  ; White";

        // 区切り文字として使う文字を配列で定義
        char[] delimiters = new char[] { ',', ';', '/', ' ' };

        // 分割と同時に空要素を削除
        string[] result = multiDelim.Split(delimiters, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);

        foreach (var color in result)
        {
            Console.WriteLine($"色: {color}");
        }
    }
}
実行結果
色: Red
色: Blue
色: Green
色: Yellow
色: White

このように、複数の区切り文字が連続していても、RemoveEmptyEntriesが適切に機能し、純粋な値だけを抽出できます。

特定の文字列(String)を区切り文字にする場合

文字(char)ではなく、文字列(string)を区切りとして使いたい場合も、StringSplitOptionsは同様に使用可能です。

文字列での分割例

例えば、ログファイルなどで"---"のような特定の記号列を区切りとしている場合です。

C#
using System;

class Program
{
    static void Main()
    {
        string logData = "INFO---Error---Warning------Debug";

        // 文字列の配列を区切り文字として指定
        string[] separators = new string[] { "---" };

        string[] levels = logData.Split(separators, StringSplitOptions.RemoveEmptyEntries);

        foreach (var level in levels)
        {
            Console.WriteLine(level);
        }
    }
}
実行結果
INFO
Error
Warning
Debug

文字列での分割では、---が2回連続して------となっている箇所がありますが、ここも正しく1つの区切りとして処理され、空の要素は発生しません。

パフォーマンスと注意点

便利なRemoveEmptyEntriesですが、大規模なデータを扱う際にはいくつかの考慮すべき点があります。

メモリの割り当て

Splitメソッドは、分割結果を新しい配列として生成します。

元の文字列が非常に大きく、区切り文字が大量に含まれる場合、新しい配列とそれに対応する多数の文字列オブジェクトがヒープメモリに確保されます。

もし、分割した結果を1回ループで回すだけであれば、.NET Core 2.1以降で導入されたSpan<T>MemoryExtensions.EnumerateSplitsといった、よりメモリ効率の良いAPIを検討する価値があります。

ただし、通常のアプリケーション開発においては、SplitRemoveEmptyEntriesの組み合わせが最も直感的でメンテナンス性が高いと言えます。

LINQとの使い分け

以前のC#では以下のようなコードがよく書かれていました。

C#
// 昔ながらの書き方
var result = input.Split(',').Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray();

この書き方でも同じ結果が得られますが、「配列を生成してから、さらにLINQでフィルタリングし、最後にToArrayでまた配列を作る」というステップを踏むため、オーバーヘッドが大きくなります。

StringSplitOptionsを使える環境であれば、メソッドの引数で指定する方がパフォーマンス面でも可読性面でも優れています

まとめ

C#のSplitメソッドにおける空要素の扱いは、データの正確性を保つ上で非常に重要です。

  • RemoveEmptyEntriesを使うことで、連続した区切り文字によって発生する不要な空要素を排除できる。
  • .NET 5以降であれば、TrimEntriesを併用することで、前後の空白除去も同時に行える。
  • ビット演算子(|)を使って複数のオプションを組み合わせるのが、現代的なC#のスタイル。
  • 文字だけでなく、文字列の配列を区切り文字に指定することも可能。

これらの機能を使いこなすことで、文字列解析のコードをより短く、バグの少ない堅牢なものにすることができます。

入力データの形式が不安定な外部ファイルやユーザー入力を扱う際には、ぜひこれらのオプションを積極的に活用してみてください。

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

URLをコピーしました!