ここでは、2つの文字列が等しいか(つまり、2つのString型データが値等価であるか)を調べる方法について説明します。
なお、値等価と参照等価の違いや、等価を調べる基本的な方法については、「2つの値が等しいか調べる、等値演算子(==)とEqualsメソッドの違い」で説明しています。また、文字列が空かを調べる(NULLや空の文字列と比較する)方法は、「文字列が空かどうか調べる」で説明しています。
Stringは参照型ですが、等値演算子(VB.NETでは=、C#では==)を使って、値の等価を調べることができます。
等値演算子で文字列を比較すると、2つの文字列が全く同じかを確認できます。つまり、大文字小文字を区別し、カルチャにも依存しない比較(序数比較、バイナリ比較)を行います。
ただし、VB.NETでは、Option Compareステートメントに"Text"を設定(または、/optioncompareコンパイラオプションを"text"に設定)することにより、=演算子で大文字小文字を区別しない比較が行われるようになります。
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"が指定されているときは、"等しいです。"
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メソッドで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になります。
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
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)は空の文字列("")と同じとして扱われるようです。詳しくは、こちらをご覧ください。
Dim s1 As String = "" Dim s2 As String = "〇" Console.WriteLine(s1.Equals(s2, StringComparison.CurrentCulture)) 'Trueとなる
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"カルチャで大文字小文字を区別しないで文字列を比較する例を示します。
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
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で組み合わせることもできます。
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 '結果は、"等しいです。"と表示される
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"は等しい。 |
上記で紹介したもの以外にも、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に慣れた人が使いやすいという以外に、積極的に使用する理由は無さそうです。
大文字と小文字を区別しないで比較するには、比較する2つの文字列をToLowerやToUpper(または、.NET Framework 2.0以降では、ToLowerInvariantやToUpperInvariant)で小文字や大文字に変換してから、等値演算子やEqualsメソッドなどの序数比較を行うという方法もあります。
このような方法よりも上述したStringComparison列挙体を使った方法を使うべきですが、どうしても大文字か小文字かに合わせたい(正規化したい)のであれば、String.ToUpperInvariantメソッドを使うように「.NET Framework で文字列を使用するためのベスト プラクティス」では推奨しています。
Dim s1 As String = "abc" Dim s2 As String = "Abc" '大文字小文字を区別しないで比較する If s1.ToUpperInvariant() = s2.ToUpperInvariant() Then Console.WriteLine("等しいです。") End If
string s1 = "abc"; string s2 = "Abc"; //大文字小文字を区別しないで比較する if (s1.ToUpperInvariant() == s2.ToUpperInvariant()) { Console.WriteLine("等しいです。"); }
序数比較を行う等値演算子とEqualsメソッドの速度比較では、先に述べたように、等値演算子の実装がEqualsメソッドを呼び出しているため、通常はEqualsメソッドの方が速いと考えてよさそうです。ただし、こちらで紹介した「String.Empty vs. ""」によると、空の文字列同士の比較では、等値演算子の方が速いそうです。ただしこれはC#の結果ですので、VB.NETでも同じかは分かりません。
この内容は、「文字列内に指定された文字列があるか調べ、その位置を知る」に移しました。
この内容は、「文字列内に指定された文字列があるか調べ、その位置を知る」に移しました。
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。