ここではStringクラスに用意されている文字列探索・比較の操作を行うためのメソッドについて見ていきます。 なお、探索・比較時のオプションとカルチャの規則に基づいた比較については、文字列と比較オプション・カルチャの並べ替え規則で解説します。

ここで解説する文字列の探索・比較の操作と、対応するStringクラスのメソッドを表にまとめます。 個々のメソッドの詳細については順に解説していきます。

文字列操作と対応するStringクラスのメソッド
文字列操作 対応するStringクラスのメソッド 解説へのリンク
部分文字列の探索
(部分文字列の位置の取得)
IndexOf (前方からの探索)
LastIndexOf (後方からの探索)
解説へ
任意の一文字の探索
(文字の位置の取得)
IndexOfAny (前方からの探索)
LastIndexOfAny (後方からの探索)
解説へ
部分文字列を含むかどうかの判定
(部分一致の判定)
Contains 解説へ
部分文字列で始まるかどうかの判定
(前方一致の判定)
StartsWith 解説へ
部分文字列で終わるかどうかの判定
(後方一致の判定)
EndsWith 解説へ
nullまたは空の文字列かどうかの判定 IsNullOrEmpty 解説へ
null、空の文字列、または空白のみかどうかの判定 IsNullOrWhiteSpace 解説へ
文字列が等しいかどうかの判定 Equals
等価演算子、不等価演算子
解説へ
解説へ
文字列の大小関係の比較 Compare, CompareTo 解説へ
序数による文字列の大小関係の比較 CompareOrdinal 解説へ
文字列操作 対応するStringクラスのメソッド 解説へのリンク

Stringクラスのメソッドではワイルドカードのようなパターンマッチングを使った文字列の探索・比較は出来ないので、そういった場合は正規表現(Regexクラス)を使います。

なお、文字列の加工と編集については文字列の加工・編集で解説しています。 また、この文章ではUnicode正規形や正規化、サロゲートペアを含む文字列には踏み込みません。 必要に応じて他のドキュメントを参照してください。

§1 文字列の探索

ここでは文字列の探索などを行うためのメソッドについて見ていきます。

§1.1 部分文字列の探索 (IndexOf, LastIndexOf)

String.IndexOfメソッドは文字列内にある部分文字列の位置を探索するメソッドで、文字列内にある部分文字列の最初のインデックスを返します。 文字列内に部分文字列がない場合は-1が返されます。

using System;

class Sample {
  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    Console.WriteLine(s.IndexOf("fox"));
    Console.WriteLine(s.IndexOf("lazy dog"));
    Console.WriteLine(s.IndexOf("cat"));
  }
}
実行結果
16
35
-1

String.LastIndexOfメソッドはIndexOfメソッドとは異なり、後ろから探索を行い、文字列内にある部分文字列の最後のインデックスを返します。 文字列内に部分文字列がない場合はIndexOfメソッドと同様に-1が返されます。

using System;

class Sample {
  static void Main()
  {
    string s = "foo bar foo";

    Console.WriteLine(s.IndexOf("foo"));
    Console.WriteLine(s.LastIndexOf("foo"));
    Console.WriteLine(s.IndexOf("baz"));
    Console.WriteLine(s.LastIndexOf("baz"));
  }
}
実行結果
0
8
-1
-1

IndexOfメソッド、LastIndexOfメソッドでは文字列ではなく文字(char)の位置を探索することも出来ます。 文字列内に指定された文字がない場合は同様に-1が返されます。

using System;

class Sample {
  static void Main()
  {
    string s = "foo bar foo";

    Console.WriteLine(s.IndexOf('o'));
    Console.WriteLine(s.LastIndexOf('o'));
    Console.WriteLine(s.IndexOf('x'));
    Console.WriteLine(s.LastIndexOf('x'));
  }
}
実行結果
1
10
-1
-1

IndexOfメソッド、LastIndexOfメソッドでは、文字列比較時のオプションを指定することができ、例えば大文字小文字の違いを無視した探索を行うことも出来ます。 文字列比較時のオプションについてはStringComparisonとStringComparerで詳しく説明します。

using System;

class Sample {
  static void Main()
  {
    string s = "foo bar foo";

    Console.WriteLine(s.IndexOf("FOO", StringComparison.CurrentCulture));
    Console.WriteLine(s.IndexOf("FOO", StringComparison.CurrentCultureIgnoreCase));
    Console.WriteLine(s.LastIndexOf("FOO", StringComparison.CurrentCulture));
    Console.WriteLine(s.LastIndexOf("FOO", StringComparison.CurrentCultureIgnoreCase));
  }
}
実行結果
-1
0
-1
8

§1.2 任意の文字の探索 (IndexOfAny, LastIndexOfAny)

String.IndexOfAnyメソッドは指定された複数の文字の中で、文字列内で見つかった最初の位置を探索するメソッドです。 IndexOfメソッド同様、いずれの文字も見つからなかった場合は-1が返されます。

using System;

class Sample {
  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    // q, w, eのいずれかの文字が最初に現れる位置を取得する
    Console.WriteLine(s.IndexOfAny(new char[] {'q', 'w', 'e'}));

    // R, T, Yのいずれかの文字が最初に現れる位置を取得する
    Console.WriteLine(s.IndexOfAny(new char[] {'R', 'T', 'Y'}));
  }
}
実行結果
2
0

String.LastIndexOfAnyメソッドは、後ろから探索を行い、文字列内で見つかった最後の位置を返します。

using System;

class Sample {
  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    // a, e, oのいずれかの文字が最初に現れる位置を取得する
    Console.WriteLine(s.IndexOfAny(new char[] {'a', 'e', 'o'}));

    // a, e, oのいずれかの文字が最後に現れる位置を取得する
    Console.WriteLine(s.LastIndexOfAny(new char[] {'a', 'e', 'o'}));
  }
}
実行結果
2
41

§1.3 部分文字列の一致 (Contains, StartsWith, EndsWith)

String.Containsメソッドは文字列中に部分文字列が含まれているかどうかを調べるメソッドです。 部分文字列が含まれている場合(=IndexOfメソッドの結果が-1以外となる場合)はtrueが返されます。

using System;

class Sample {
  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    Console.WriteLine(s.Contains("fox"));
    Console.WriteLine(s.Contains("lazy dog"));
    Console.WriteLine(s.Contains("cat"));
  }
}
実行結果
True
True
False

String.StartsWithメソッドは文字列が指定された部分文字列で始まるかどうかを調べるメソッドです。 逆に、String.EndsWithメソッドは部分文字列で終わるかどうかを調べるメソッドです。

using System;

class Sample {
  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    Console.WriteLine(s.StartsWith("The"));  // 文字列が"The"で始まるか
    Console.WriteLine(s.StartsWith("lazy")); // 文字列が"lazy"で始まるか
    Console.WriteLine(s.EndsWith("dog"));    // 文字列が"dog"で終わるか
    Console.WriteLine(s.EndsWith("cat"));    // 文字列が"cat"で終わるか
  }
}
実行結果
True
False
True
False

Contains・StartsWith・EndsWithの各メソッドは、IndexOf・LastIndexOfメソッドとは異なりcharを引数にとるバージョンが用意されていないので、文字単位での一致を調べることはできません。 文字列が指定された文字で始まる/終わる/含むかどうかを調べるには、IndexOf・LastIndexOfメソッドを使う必要があります。 次の例は、IndexOf・LastIndexOfメソッドを使って、charを引数にとるContains・StartsWith・EndsWithメソッドを実装したものです。

using System;

class Sample {
  // 文字列sが指定された文字cを含むかどうか調べるメソッド
  static bool Contains(string s, char c)
  {
    return s.IndexOf(c) != -1;
  }

  // 文字列sが指定された文字cで始まるかどうか調べるメソッド
  static bool StartsWith(string s, char c)
  {
    return s.IndexOf(c) == 0;
    // もしくは単純に
    // return s[0] == c;
  }

  // 文字列sが指定された文字cで終わるかどうか調べるメソッド
  static bool EndsWith(string s, char c)
  {
    return s.LastIndexOf(c) == s.Length - 1;
    // もしくは単純に
    // return s[s.Length - 1] == c;
  }

  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    Console.WriteLine(Contains(s, 'f'));    // 文字列が'f'を含むか
    Console.WriteLine(Contains(s, 'A'));    // 文字列が'A'を含むか
    Console.WriteLine(StartsWith(s, 'T'));  // 文字列が'T'で始まるか
    Console.WriteLine(StartsWith(s, 'l'));  // 文字列が'l'で始まるか
    Console.WriteLine(EndsWith(s, 'g'));    // 文字列が'g'で終わるか
    Console.WriteLine(EndsWith(s, 't'));    // 文字列が't'で終わるか
  }
}
実行結果
True
False
True
False
True
False

StartsWithメソッド、EndsWithメソッドでは、文字列比較時のオプションを指定することができ、例えば大文字小文字の違いを無視して部分文字列が一致するかどうかを調べることも出来ます。 文字列比較時のオプションについてはStringComparisonとStringComparerで詳しく説明します。

using System;

class Sample {
  static void Main()
  {
    string s = "The quick brown fox jumps over the lazy dog";

    Console.WriteLine(s.StartsWith("the", StringComparison.CurrentCulture));
    Console.WriteLine(s.StartsWith("the", StringComparison.CurrentCultureIgnoreCase));
    Console.WriteLine(s.EndsWith("DOG", StringComparison.CurrentCulture));
    Console.WriteLine(s.EndsWith("DOG", StringComparison.CurrentCultureIgnoreCase));
  }
}
実行結果
False
True
False
True


§2 文字列の比較・等価性の検証

ここでは文字列の比較・等価性の検証などを行うためのメソッドについて見ていきます。

§2.1 null、空文字、空白のチェック (IsNullOrEmpty, IsNullOrWhiteSpace)

String.IsNullOrEmptyメソッドは文字列がnull/Nothingもしくは空文字かどうかをチェックする静的メソッドです。 String.IsNullOrWhiteSpaceメソッドは.NET Framework 4から追加されたメソッドで、文字列がnull/Nothingもしくは空文字か、空白文字のみで構成されているかどうかをチェックする静的メソッドです。

using System;

class Sample {
  static void Main()
  {
    string s = "foo";

    Console.WriteLine("<{0}> : IsNullOrEmpty = {1}", s, String.IsNullOrEmpty(s));
    Console.WriteLine("<{0}> : IsNullOrWhiteSpace = {1}", s, String.IsNullOrWhiteSpace(s));

    s = "   "; // 空白のみの文字列

    Console.WriteLine("<{0}> : IsNullOrEmpty = {1}", s, String.IsNullOrEmpty(s));
    Console.WriteLine("<{0}> : IsNullOrWhiteSpace = {1}", s, String.IsNullOrWhiteSpace(s));

    s = ""; // 空(長さ0)の文字列

    Console.WriteLine("<{0}> : IsNullOrEmpty = {1}", s, String.IsNullOrEmpty(s));
    Console.WriteLine("<{0}> : IsNullOrWhiteSpace = {1}", s, String.IsNullOrWhiteSpace(s));

    s = null; // nullが代入されている文字列変数

    Console.WriteLine("<{0}> : IsNullOrEmpty = {1}", s, String.IsNullOrEmpty(s));
    Console.WriteLine("<{0}> : IsNullOrWhiteSpace = {1}", s, String.IsNullOrWhiteSpace(s));
  }
}
実行結果
<foo> : IsNullOrEmpty = False
<foo> : IsNullOrWhiteSpace = False
<   > : IsNullOrEmpty = False
<   > : IsNullOrWhiteSpace = True
<> : IsNullOrEmpty = True
<> : IsNullOrWhiteSpace = True
<> : IsNullOrEmpty = True
<> : IsNullOrWhiteSpace = True

このメソッドでは、半角および全角のスペースやタブだけでなく、改行文字なども空白文字として扱われます。 具体的には、Unicodeで空白文字と定義されている文字(Char.IsWhiteSpacetrueとなる文字)が空白文字として扱われます。

なお、IsNullOrEmptyメソッド、IsNullOrWhiteSpaceメソッドの動作は、次のコードと等価です。

IsNullOrEmptyメソッド・IsNullOrWhiteSpaceメソッドと同じ動作を行うメソッドの実装
static bool IsNullOrEmpty(string s)
{
  if (s == null)
    return true;
  else if (s.Length == 0)
    return true;
  else
    return false;
}

static bool IsNullOrWhiteSpace(string s)
{
  if (IsNullOrEmpty(s))
    return true;
  else if (s.Trim().Length == 0)
    return true;
  else
    return false;
}

§2.2 比較 (CompareTo, Equals, Compare)

String.CompareToメソッドString.Equalsメソッドは文字列の比較を行うメソッドで、指定された文字列と比較した結果を返します。

CompareToメソッドの戻り値は、二つの文字列を並べ替えた時の順番に従い、次のようになります。 比較の際、null/Nothingは空文字を含むどのような文字列よりも小さいと判断され、null/Nothing同士は等価として扱われます。

String.CompareToの戻り値
文字列aとbの関係 a.CompareTo(b)の戻り値
並べ替えたときにaの方がbよりも前
(aはbよりも小さい)
0より小さい値 "ABC" < "ABD"
null < "ABC"
並べ替えたときにaとbは同じ位置
(aとbは等しい)
0 "ABC" = "ABC"
null = null
並べ替えたときにaの方がbよりも後
(aはbよりも大きい)
0より大きい値 "ABC" > "ABB"
"ABC" > null

Equalsメソッドは、二つの文字列が等しい場合(CompareToメソッドが0を返す場合)にTrueを返します。

using System;

class Sample {
  static void Main()
  {
    string s1 = "foo";
    string s2 = "bar";
    string s3 = "baz";

    Console.WriteLine("{0} CompareTo {1} : {2}", s1, s2, s1.CompareTo(s2));
    Console.WriteLine("{0} CompareTo {1} : {2}", s1, s3, s1.CompareTo(s3));
    Console.WriteLine("{0} CompareTo {1} : {2}", s2, s3, s2.CompareTo(s3));
    Console.WriteLine("{0} CompareTo {1} : {2}", s2, s2, s2.CompareTo(s2));

    Console.WriteLine("{0} Equals {1} : {2}", s1, s2, s1.Equals(s2));
    Console.WriteLine("{0} Equals {1} : {2}", s1, s3, s1.Equals(s3));
    Console.WriteLine("{0} Equals {1} : {2}", s2, s3, s2.Equals(s3));
    Console.WriteLine("{0} Equals {1} : {2}", s2, s2, s2.Equals(s2));
  }
}
実行結果
foo CompareTo bar : 1
foo CompareTo baz : 1
bar CompareTo baz : -1
bar CompareTo bar : 0
foo Equals bar : False
foo Equals baz : False
bar Equals baz : False
bar Equals bar : True

String.Compareメソッドは、CompareToメソッドと同じ動作をする静的メソッドです。 Equalsメソッドは、静的メソッドとしても用意されています。 戻り値はインスタンスメソッドの場合と同じです。

String.Compareの戻り値
文字列aとbの関係 String.Compare(a, b)の戻り値
並べ替えたときにaの方がbよりも前
(aはbよりも小さい)
0より小さい値 "ABC" < "ABD"
null < "ABC"
並べ替えたときにaとbは同じ位置
(aとbは等しい)
0 "ABC" = "ABC"
null = null
並べ替えたときにaの方がbよりも後
(aはbよりも大きい)
0より大きい値 "ABC" > "ABB"
"ABC" > null
using System;

class Sample {
  static void Main()
  {
    string s1 = "foo";
    string s2 = "bar";
    string s3 = "baz";

    Console.WriteLine("Compare({0}, {1}) : {2}", s1, s2, String.Compare(s1, s2));
    Console.WriteLine("Compare({0}, {1}) : {2}", s1, s3, String.Compare(s1, s3));
    Console.WriteLine("Compare({0}, {1}) : {2}", s2, s3, String.Compare(s2, s3));
    Console.WriteLine("Compare({0}, {1}) : {2}", s2, s2, String.Compare(s2, s2));

    Console.WriteLine("Equals({0}, {1}) : {2}", s1, s2, String.Equals(s1, s2));
    Console.WriteLine("Equals({0}, {1}) : {2}", s1, s3, String.Equals(s1, s3));
    Console.WriteLine("Equals({0}, {1}) : {2}", s2, s3, String.Equals(s2, s3));
    Console.WriteLine("Equals({0}, {1}) : {2}", s2, s2, String.Equals(s2, s2));
  }
}
実行結果
Compare(foo, bar) : 1
Compare(foo, baz) : 1
Compare(bar, baz) : -1
Compare(bar, bar) : 0
Equals(foo, bar) : False
Equals(foo, baz) : False
Equals(bar, baz) : False
Equals(bar, bar) : True

このように、インスタンスメソッドでも静的メソッドでも結果は同じですが、想定される状況により使い分けることが出来ます。 例えば、静的メソッドのCompareとEqualsを使うと、仮に文字列変数にnull/Nothingが代入されていてもヌル参照を引き起こさずに済みます。 (nullとの比較、null同士の比較を行っても例外エラーにはなりません)

using System;

class Sample {
  static void Main()
  {
    string s1 = null;
    string s2 = "foo";

    Console.WriteLine(String.Compare(s1, s2)); // エラーにはならない
    Console.WriteLine(s1.CompareTo(s2)); // NullReferenceExceptionがスローされる
  }
}

CompareToメソッド、Compareメソッド、Equalsメソッドでは、文字列比較時のオプションを指定することができ、例えば大文字小文字の違いを無視した比較を行うことも出来ます。 文字列比較時のオプションについてはStringComparisonとStringComparerで詳しく説明します。

using System;

class Sample {
  static void Main()
  {
    string s1 = "Bar";
    string s2 = "bar";
    string s3 = "BAR";

    Console.WriteLine("Compare({0}, {1}) : {2}", s1, s2, String.Compare(s1, s2, StringComparison.CurrentCulture));
    Console.WriteLine("Compare({0}, {1}) : {2}", s1, s2, String.Compare(s1, s2, StringComparison.CurrentCultureIgnoreCase));
    Console.WriteLine("Compare({0}, {1}) : {2}", s1, s3, String.Compare(s1, s3, StringComparison.CurrentCulture));
    Console.WriteLine("Compare({0}, {1}) : {2}", s1, s3, String.Compare(s1, s3, StringComparison.CurrentCultureIgnoreCase));

    Console.WriteLine("Equals({0}, {1}) : {2}", s1, s2, String.Equals(s1, s2, StringComparison.CurrentCulture));
    Console.WriteLine("Equals({0}, {1}) : {2}", s1, s2, String.Equals(s1, s2, StringComparison.CurrentCultureIgnoreCase));
    Console.WriteLine("Equals({0}, {1}) : {2}", s1, s3, String.Equals(s1, s3, StringComparison.CurrentCulture));
    Console.WriteLine("Equals({0}, {1}) : {2}", s1, s3, String.Equals(s1, s3, StringComparison.CurrentCultureIgnoreCase));

  }
}
実行結果
Compare(Bar, bar) : 1
Compare(Bar, bar) : 0
Compare(Bar, BAR) : -1
Compare(Bar, BAR) : 0
Equals(Bar, bar) : False
Equals(Bar, bar) : True
Equals(Bar, BAR) : False
Equals(Bar, BAR) : True

§2.3 序数による比較 (CompareOrdinal)

String.CompareOrdinalメソッドは、Compareメソッド同様に文字列の比較を行う静的メソッドですが、比較の際に文字列の各文字を数値(Unicode序数Unicode ordinal)、つまり各文字のコードポイントでの比較を行う点で動作が異なります。

Compareメソッドが現在のスレッドの(もしくは引数で指定された)カルチャの規則にしたがった比較を行うのに対し、CompareOrdinalメソッドではカルチャに依存しない比較が行われます。

using System;

class Sample {
  static void Main()
  {
    string[] p1 = new string[] {"abc", "abd"};
    string[] p2 = new string[] {"coop", "co-op"};
    string[] p3 = new string[] {"cant", "can't"};

    Console.WriteLine("Compare       ({0}, {1}) : {2}", p1[0], p1[1], String.Compare(p1[0], p1[1]));
    Console.WriteLine("CompareOrdinal({0}, {1}) : {2}", p1[0], p1[1], String.CompareOrdinal(p1[0], p1[1]));

    Console.WriteLine("Compare       ({0}, {1}) : {2}", p2[0], p2[1], String.Compare(p2[0], p2[1]));
    Console.WriteLine("CompareOrdinal({0}, {1}) : {2}", p2[0], p2[1], String.CompareOrdinal(p2[0], p2[1]));

    Console.WriteLine("Compare       ({0}, {1}) : {2}", p3[0], p3[1], String.Compare(p3[0], p3[1]));
    Console.WriteLine("CompareOrdinal({0}, {1}) : {2}", p3[0], p3[1], String.CompareOrdinal(p3[0], p3[1]));
  }
}
実行結果
Compare       (abc, abd) : -1
CompareOrdinal(abc, abd) : -1
Compare       (coop, co-op) : -1
CompareOrdinal(coop, co-op) : 66
Compare       (cant, can't) : -1
CompareOrdinal(cant, can't) : 77

CompareメソッドとCompareOrdinalメソッドで大小関係が反転している箇所に注目してください。 他のメソッドでも、CompareOrdinalと同様の比較方法を使用するように指定できます。 これについてはStringComparisonとStringComparerで詳しく説明します。

§2.4 等価演算子(==, !=, <>)

C#、VB.NETでは等価演算子・不等価演算子を用いることでEqualsメソッドと同等の比較が行えます。 C#、VB.NETでのstring同士の等価演算子の結果は、String.Equals(a, b)と同じ動作となります。 不等価演算子は、String.Equals(a, b)の結果を否定(反転)したものと同じです。

using System;

class Sample {
  static void Main()
  {
    string s1 = "foo";
    string s2 = "bar";
    string s3 = "foo";

    Console.WriteLine("{0} == {1}: {2}", s1, s2, s1 == s2);
    Console.WriteLine("{0} != {1}: {2}", s1, s2, s1 != s2);
    Console.WriteLine("{0} == {1}: {2}", s1, s3, s1 == s3);
    Console.WriteLine("{0} != {1}: {2}", s1, s3, s1 != s3);
  }
}
実行結果
foo == bar: False
foo != bar: True
foo == foo: True
foo != foo: False

当然ながら、等価演算子・不等価演算子による比較の場合は、カルチャを無視したり大文字小文字を無視したりといった文字列比較時のオプション(StringComparison)を指定することはできません。 文字列比較時のオプションを指定する必要がある場合はEqualsメソッドを用います。

また、C#において、等価演算子・不等価演算子がオーバーロードされていない限り、両辺がstringでないとコンパイルエラーとなります(タイプセーフ)。 一方、Equalsメソッドは引数にObject型をとるオーバーロードも用意されているため、stringと異なる型の比較を行おうとしてもコンパイルエラーにはなりません。

using System;

class Sample {
  static void Main()
  {
    string s = "16";
    int i = 16;

    // 等価演算子では、stringとintの比較はコンパイルエラーとなる
    Console.WriteLine(s == i); // error CS0019: 演算子 '==' を 'string' と 'int' 型のオペランドに適用することはできません。

    // Equalsメソッドでは、stringとintの比較はコンパイルエラーにはならない
    // また実行時にも例外はスローされない
    Console.WriteLine(String.Equals(s, i));
  }
}

等価演算子とEqualsメソッドではパフォーマンスに若干の違いはありますが、どちらを使うのが妥当かは状況により異なるので、両者の違いを十分に考慮して選択する必要があります。

§2.5 比較演算子 (<, <=, >, >=, Like)

(このドキュメントは未整理です)

VB.NETでは、不等号演算子、Like演算子を用いることでCompareメソッドと同等の比較が行えます。 比較時の動作は、Option Compareステートメントの設定により変わります。

C#では、不等号演算子による文字列の比較は行えないので、Compareメソッドを使用する必要があります。