DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

2つの文字列が等しいかを調べる
大文字小文字、半角全角、ひらがなカタカナの区別をしないで文字列を比較する

ここでは、2つの文字列が等しいか(つまり、2つのString型データが値等価であるか)を調べる方法について説明します。

なお、値等価と参照等価の違いや、等価を調べる基本的な方法については、「2つの値が等しいか調べる、等値演算子(==)とEqualsメソッドの違い」で説明しています。また、文字列が空かを調べる(NULLや空の文字列と比較する)方法は、「文字列が空かどうか調べる」で説明しています。

等値演算子

Stringは参照型ですが、等値演算子(VB.NETでは=、C#では==)を使って、値の等価を調べることができます。

等値演算子で文字列を比較すると、2つの文字列が全く同じかを確認できます。つまり、大文字小文字を区別し、カルチャにも依存しない比較(序数比較、バイナリ比較)を行います。

ただし、VB.NETでは、Option Compareステートメントに"Text"を設定(または、/optioncompareコンパイラオプションを"text"に設定)することにより、=演算子で大文字小文字を区別しない比較が行われるようになります。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "abc"
Dim s2 As String = "ABC"

's1とs2が等しいか調べる
If s1 = s2 Then
    Console.WriteLine("等しいです。")
Else
    Console.WriteLine("等しくないです。")
End If

'結果は、"等しくないです。"と表示される
'"Option Compare Text"が指定されているときは、"等しいです。"
C#
コードを隠すコードを選択
string s1 = "abc";
string s2 = "ABC";

//s1とs2が等しいか調べる
if (s1 == s2)
{
    Console.WriteLine("等しいです。");
}
else
{
    Console.WriteLine("等しくないです。");
}

//結果は、"等しくないです。"と表示される
補足:等値演算子は、String.Equalsメソッドを呼び出しています。ただしVB.NETでは、Option CompareステートメントがBinaryの時はString.CompareOrdinalメソッドを、Textの時は現在のカルチャのCompareInfo.Compareメソッドを呼び出しているようです。

C#では、Object型にキャストされている文字列を比較するときに==演算子を使うと、参照の等価を調べることになってしまうことに注意してください。詳しくは、こちらをご覧ください。

String.Equalsメソッド

String.Equalsメソッドで2つの文字列が等しいかを調べることもできます。String.Equalsメソッドも大文字小文字を区別して、カルチャに依存しない比較を行います。

ただし.NET Framework 2.0からは、String.EqualsメソッドにStringComparison列挙体の値を指定することができ、大文字と小文字を区別しない比較や、カルチャに依存した比較が可能になりました。

StringComparison列挙体には6つのメンバがあり、それぞれの名前と意味は以下の通りです。

メンバ名 説明(MSDN「StringComparison 列挙体」より)
CurrentCulture カルチャに依存した並べ替え規則および現在のカルチャを使用して文字列を比較します。
CurrentCultureIgnoreCase カルチャに依存した並べ替え規則および現在のカルチャを使用し、比較対象の文字列の大文字と小文字の違いは無視して文字列を比較します。
InvariantCulture カルチャに依存した並べ替え規則およびインバリアント カルチャを使用して文字列を比較します。
InvariantCultureIgnoreCase カルチャに依存した並べ替え規則およびインバリアント カルチャを使用し、比較対象の文字列の大文字と小文字の違いは無視して文字列を比較します。
Ordinal 序数の並べ替え規則を使用して文字列を比較します。
OrdinalIgnoreCase 序数の並べ替え規則を使用し、比較対象の文字列の大文字と小文字の違いは無視して文字列を比較します。

上記のStringComparison値の内、最もよく使われるのは、Ordinalです。Ordinalは単純なバイト比較を行うため、パフォーマンスに優れています。また、厳密な一致が必要なため、予想外の結果になるというトラブルが少ないです。例えばパスワードの比較では、Ordinalを使うべきです。さらに、文字列に埋め込まれたnull文字を無視しないという特徴もあります。

大文字と小文字を区別しない比較を行うときも、OrdinalIgnoreCaseを使うのが一番分かりやすく、間違いのない方法です。ファイル名の比較などは、OrdinalIgnoreCaseを使うのが良いでしょう。なおOrdinalIgnoreCaseの比較は、String.ToUpperInvariantメソッドで大文字に変換してからOrdinalで比較するのと結果的に同じです。

現在のカルチャを使用して(CurrentCultureやCurrentCultureIgnoreCaseを使用して)文字列を比較するケースとしては、ユーザーが分かりやすい形で、厳密さを要求しない比較を行う場合などがあります。

インバリアントカルチャとは、英語をベースとする、ロケールに依存しないカルチャのことです。言語的な意味を持つがカルチャには依存しない比較を行いたい時に使います。2つの文字列が等しいか調べるのにインバリアントカルチャを使用するというケースは、ほとんどありません。インバリアントカルチャを使用するケースは、文字列をSortで並び替えてBinarySearchで検索する例を「配列やコレクション内に指定された要素があるか調べ、その位置を知る」で説明しています。

さらなる補足

現在のカルチャが日本語(ja-JP)の環境で試した限りでは、どのStringComparison列挙体のメンバを使っても、全角文字と半角文字('A'と'A'、'ア'と'ア'など)を区別しないで比較することはできませんでした。

"IgnoreCase"が名前についているStringComparisonメンバを用いたときは、半角英字だけでなく、全角英字の大文字と小文字も区別しないで比較できました。

例えば下の例のようなコードで試してみると、"abc"と"ABC"(両方半角)、"abc"と"ABC"(両方全角)は大文字と小文字を無視した比較でTrueになります。それ以外は、同じ文字列同士の比較以外は、すべてFalseになります。

VB.NET
コードを隠すコードを選択
Dim ss As String() = New String() {"abc", "ABC", "abc", "ABC"}
For Each s1 As String In ss
    For Each s2 As String In ss
        Console.WriteLine(s1 + "-" + s2)
        Console.WriteLine("CurrentCulture            : {0}", _
            s1.Equals(s2, StringComparison.CurrentCulture))
        Console.WriteLine("CurrentCultureIgnoreCase  : {0}", _
            s1.Equals(s2, StringComparison.CurrentCultureIgnoreCase))
        Console.WriteLine("InvariantCulture          : {0}", _
            s1.Equals(s2, StringComparison.InvariantCulture))
        Console.WriteLine("InvariantCultureIgnoreCase: {0}", _
            s1.Equals(s2, StringComparison.InvariantCultureIgnoreCase))
        Console.WriteLine("Ordinal                   : {0}", _
            s1.Equals(s2, StringComparison.Ordinal))
        Console.WriteLine("OrdinalIgnoreCase         : {0}", _
            s1.Equals(s2, StringComparison.OrdinalIgnoreCase))
        Console.WriteLine()
    Next
Next
C#
コードを隠すコードを選択
string[] ss = new string[] {"abc", "ABC", "abc", "ABC"};
foreach (string s1 in ss)
{
    foreach (string s2 in ss)
    {
        Console.WriteLine(s1 + "-" + s2);
        Console.WriteLine("CurrentCulture            : {0}",
            s1.Equals(s2, StringComparison.CurrentCulture));
        Console.WriteLine("CurrentCultureIgnoreCase  : {0}",
            s1.Equals(s2, StringComparison.CurrentCultureIgnoreCase));
        Console.WriteLine("InvariantCulture          : {0}",
            s1.Equals(s2, StringComparison.InvariantCulture));
        Console.WriteLine("InvariantCultureIgnoreCase: {0}",
            s1.Equals(s2, StringComparison.InvariantCultureIgnoreCase));
        Console.WriteLine("Ordinal                   : {0}",
            s1.Equals(s2, StringComparison.Ordinal));
        Console.WriteLine("OrdinalIgnoreCase         : {0}",
            s1.Equals(s2, StringComparison.OrdinalIgnoreCase));
        Console.WriteLine();
    }
}

StringComparison.Ordinalを指定したときは、StringComparison値を何も指定しなかったときと同じ結果になりますが、「.NET Framework で文字列を使用するためのベスト プラクティス」では、StringComparison.Ordinalを指定してEqualsメソッドを呼び出すことを推奨しています。

名前に"Ordinal"が付くStringComparison以外を指定したときは、後述する、CompareInfo.Compareメソッドを呼び出しているようです。

補足:カルチャに依存した文字列の比較(Ordinalでない比較)を行った場合、"〇"(U+3007)は空の文字列("")と同じとして扱われるようです。詳しくは、こちらをご覧ください。
VB.NET
コードを隠すコードを選択
Dim s1 As String = ""
Dim s2 As String = "〇"
Console.WriteLine(s1.Equals(s2, StringComparison.CurrentCulture))
'Trueとなる 
C#
コードを隠すコードを選択
string s1 = "";
string s2 = "〇";
Console.WriteLine(s1.Equals(s2, StringComparison.CurrentCulture));
//Trueとなる

指定したカルチャに依存した比較を行う

String.Equalsメソッドでは、現在のカルチャまたはインバリアントカルチャを使用して比較することができました。これ以外のカルチャに依存した比較を行いたいとき(あるいは、.NET Framework 1.1以下で、StringComparisonが使えないとき)は、CompareInfo.Compareメソッドを使います。Compareメソッドが0を返したときに等価と判断できます。

"ja-JP"カルチャで大文字小文字を区別しないで文字列を比較する例を示します。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "abc"
Dim s2 As String = "ABC"

'CompareInfoを作成 
Dim ci As System.Globalization.CompareInfo = _
    System.Globalization.CompareInfo.GetCompareInfo("ja-JP")
'または、次のようにもできる 
'Dim c As New System.Globalization.CultureInfo("ja-JP")
'Dim ci As System.Globalization.CompareInfo = c.CompareInfo

'大文字と小文字の区別を無視して、s1とs2が等しいか調べる 
If ci.Compare(s1, s2, System.Globalization.CompareOptions.IgnoreCase) = 0 Then
    Console.WriteLine("等しいです。")
Else
    Console.WriteLine("等しくないです。")
End If
C#
コードを隠すコードを選択
string s1 = "abc";
string s2 = "ABC";

//CompareInfoを作成
System.Globalization.CompareInfo ci =
    System.Globalization.CompareInfo.GetCompareInfo("ja-JP");
//または、次のようにもできる
//System.Globalization.CultureInfo c = 
//    new System.Globalization.CultureInfo("ja-JP");
//System.Globalization.CompareInfo ci = c.CompareInfo;

//大文字と小文字の区別を無視して、s1とs2が等しいか調べる
if (ci.Compare(s1, s2, System.Globalization.CompareOptions.IgnoreCase) == 0)
{
    Console.WriteLine("等しいです。");
}
else
{
    Console.WriteLine("等しくないです。");
}

全角と半角を区別しない、ひらがなとカタカナを区別しないなど

CompareInfo.Compareメソッドに適当なCompareOptions列挙体の値を渡すことで、全角と半角を区別しない比較や、ひらがなとカタカナを区別しない比較などを行うことができます。

全角と半角を区別しないで比較するには、CompareOptions.IgnoreWidthを指定します。ひらがなとカタカナを区別しないならば、CompareOptions.IgnoreKanaTypeです。複数のCompareOptions値をOrで組み合わせることもできます。

VB.NET
コードを隠すコードを選択
Dim ci As System.Globalization.CompareInfo = _
    System.Globalization.CultureInfo.CurrentCulture.CompareInfo

Dim s1 As String = "アイウエオ"
Dim s2 As String = "アイウエオ"
'全角と半角の区別を無視して、s1とs2が等しいか調べる 
If ci.Compare(s1, s2, System.Globalization.CompareOptions.IgnoreWidth) = 0 Then
    Console.WriteLine("等しいです。")
Else
    Console.WriteLine("等しくないです。")
End If
'結果は、"等しいです。"と表示される 

Dim s3 As String = "あいうえお"
Dim s4 As String = "アイウエオ"
'ひらがなとカタカナの区別を無視して、s3とs4が等しいか調べる 
If ci.Compare(s3, s4, System.Globalization.CompareOptions.IgnoreKanaType) = 0 Then
    Console.WriteLine("等しいです。")
Else
    Console.WriteLine("等しくないです。")
End If
'結果は、"等しいです。"と表示される 

Dim s5 As String = "あいうえお"
Dim s6 As String = "アイウエオ"
'全角と半角、ひらがなとカタカナの区別を無視して、s5とs6が等しいか調べる 
If ci.Compare(s5, s6, System.Globalization.CompareOptions.IgnoreWidth Or _
              System.Globalization.CompareOptions.IgnoreKanaType) = 0 Then
    Console.WriteLine("等しいです。")
Else
    Console.WriteLine("等しくないです。")
End If
'結果は、"等しいです。"と表示される 
C#
コードを隠すコードを選択
System.Globalization.CompareInfo ci =
    System.Globalization.CultureInfo.CurrentCulture.CompareInfo;

string s1 = "アイウエオ";
string s2 = "アイウエオ";
//全角と半角の区別を無視して、s1とs2が等しいか調べる
if (ci.Compare(s1, s2, System.Globalization.CompareOptions.IgnoreWidth) == 0)
{
    Console.WriteLine("等しいです。");
}
else
{
    Console.WriteLine("等しくないです。");
}
//結果は、"等しいです。"と表示される

string s3 = "あいうえお";
string s4 = "アイウエオ";
//ひらがなとカタカナの区別を無視して、s3とs4が等しいか調べる
if (ci.Compare(s3, s4, System.Globalization.CompareOptions.IgnoreKanaType) == 0)
{
    Console.WriteLine("等しいです。");
}
else
{
    Console.WriteLine("等しくないです。");
}
//結果は、"等しいです。"と表示される

string s5 = "あいうえお";
string s6 = "アイウエオ";
//全角と半角、ひらがなとカタカナの区別を無視して、s5とs6が等しいか調べる
if (ci.Compare(s5, s6, System.Globalization.CompareOptions.IgnoreWidth |
    System.Globalization.CompareOptions.IgnoreKanaType) == 0)
{
    Console.WriteLine("等しいです。");
}
else
{
    Console.WriteLine("等しくないです。");
}
//結果は、"等しいです。"と表示される

上記の例では現在のカルチャ(日本語)のCompareInfoを使用しましたが、インバリアントカルチャでも、全角と半角、ひらがなとカタカナの区別なしに比較できるようでした。

CompareOptions列挙体のメンバとその意味は、以下の通りです。

メンバ名 説明(MSDN「CompareOptions 列挙体」より)
None 文字列比較の既定のオプション設定を示します。 "a"と"a"は等しい。
IgnoreCase 文字列比較で大文字と小文字の区別を無視することを示します。 "a"と"A"は等しい。
IgnoreNonSpace 文字列比較で、発音区別符など、非スペーシング組み合わせ文字を無視するように指定します。Unicode 標準は、基本文字を組み合わせて生成される新しい文字を組み合わせ文字として定義しています。非スペーシング組み合わせ文字は、表示されるときに文字間隔用の領域は確保しません。非スペーシング組み合わせ文字の詳細については、Unicode のホーム ページの「The Unicode Standard」を参照してください。 "か"と"が"は等しい。
"かー"と"かぁ"は等しい。
IgnoreSymbols 文字列比較において、空白文字、句読点、通貨記号、パーセント記号、算術記号、アンパサンドなどの記号を無視することを示します。 "aa"と"a a"は等しい。
"あ"と"あ。"は等しい。
"abcd"と"%a$b\c=d+"は等しい。
IgnoreKanaType 文字列比較でカナ型を無視することを示します。カナ型とは、日本語の発音を表すひらがなとカタカナの文字を指します。ひらがなは、本来の日本語の表現と単語に使用し、カタカナは "コンピュータ" または "インターネット" などの外来語に使用します。発音は、ひらがなとカタカナのどちらでも表現できます。この値が選択されている場合、ある発音を示すひらがなは、同じ発音を示すカタカナと同一であると見なされます。 "あ"と"ア"は等しい。
IgnoreWidth 文字列比較において、半角と全角の区別を無視することを示します。たとえば、日本語のカタカナ文字は、全角または半角で記述できます。この値を選択した場合、全角で記述されたカタカナ文字は、半角で記述されたカタカナ文字と同一であると見なされます。"A"と"A"は等しい。
"ア"と"ア"は等しい。
OrdinalIgnoreCase 文字列の比較で大文字と小文字の違いを無視し、通常の比較を実行する必要があります。この手法は、インバリアント カルチャを使用して文字列を大文字に変換し、その結果に対して序数に基づく比較を実行することと同じです。 "a"と"A"は等しい。
StringSort 文字列の比較時に、文字列での並べ替えアルゴリズムを使用することを示します。文字列での並べ替えでは、ハイフン、アポストロフィ、およびその他の英数字以外の記号が英数字よりも前に来ます。 "a"と"a"は等しい。
Ordinal 各文字の Unicode 値を使用して、文字列を比較することを示します。この比較は高速ですが、カルチャに応じた処理は行いません。xxxx が yyyy よりも小さい場合、"U+xxxx" で始まる文字列は "U+yyyy" で始まる文字列よりも前になります。この値を他の CompareOptions 値と組み合わせることはできません。この値は単独で使用してください。 "a"と"a"は等しい。

String.Compare、String.CompareTo、StrCompなど

上記で紹介したもの以外にも、2つの文字列を比較する方法は多々あります。

String.Compareメソッドは、2つの文字列の大きさを比較し、等しければ0を返します。大文字小文字を区別するかや、比較に使用するカルチャを指定できます。String.Compareメソッドは、CompareInfo.Compareメソッドを呼び出しているようです。カルチャの指定があれば、そのカルチャを使い、無ければ、現在のカルチャを使います。

String.CompareToメソッドも、2つの文字列の大きさを比較し、等しければ0を返します。大文字小文字の区別をして、カルチャに依存します。String.CompareToメソッドも現在のカルチャのCompareInfo.Compareメソッドを呼び出しているようで、CompareOptions.Noneオプションが指定されています。

String.CompareOrdinalメソッドもまた、現在のカルチャのCompareInfo.Compareメソッドを呼び出しているようです。オプションには、CompareOptions.Ordinalが指定されます。

これらのメソッドは、基本的には、文字列を並び替えたときの順番の決定に使用するものですので、文字列の等価を確認する目的で使用するのは良くありません。(CompareInfo.CompareメソッドはString.Equalsメソッドで使われているのですが...。)

VB.NETでは、StrComp関数を使用することもできます。ただしこの関数は、VB6に慣れた人が使いやすいという以外に、積極的に使用する理由は無さそうです。

ToLowerまたはToUpperで変換することにより、大文字、小文字を区別しないで比較する

大文字と小文字を区別しないで比較するには、比較する2つの文字列をToLowerやToUpper(または、.NET Framework 2.0以降では、ToLowerInvariantやToUpperInvariant)で小文字や大文字に変換してから、等値演算子やEqualsメソッドなどの序数比較を行うという方法もあります。

このような方法よりも上述したStringComparison列挙体を使った方法を使うべきですが、どうしても大文字か小文字かに合わせたい(正規化したい)のであれば、String.ToUpperInvariantメソッドを使うように「.NET Framework で文字列を使用するためのベスト プラクティス」では推奨しています。

VB.NET
コードを隠すコードを選択
Dim s1 As String = "abc"
Dim s2 As String = "Abc"

'大文字小文字を区別しないで比較する 
If s1.ToUpperInvariant() = s2.ToUpperInvariant() Then
    Console.WriteLine("等しいです。")
End If
C#
コードを隠すコードを選択
string s1 = "abc";
string s2 = "Abc";

//大文字小文字を区別しないで比較する
if (s1.ToUpperInvariant() == s2.ToUpperInvariant())
{
    Console.WriteLine("等しいです。");
}

等値演算子とEqualsメソッドのどちらが速いか?

序数比較を行う等値演算子とEqualsメソッドの速度比較では、先に述べたように、等値演算子の実装がEqualsメソッドを呼び出しているため、通常はEqualsメソッドの方が速いと考えてよさそうです。ただし、こちらで紹介した「String.Empty vs. ""」によると、空の文字列同士の比較では、等値演算子の方が速いそうです。ただしこれはC#の結果ですので、VB.NETでも同じかは分かりません。

文字列の一部が一致しているか調べる

この内容は、「文字列内に指定された文字列があるか調べ、その位置を知る」に移しました。

正規表現やLike演算子

この内容は、「文字列内に指定された文字列があるか調べ、その位置を知る」に移しました。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。