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

文字列の配列やコレクションを並び替える

配列やコレクション内の要素を並び替える方法は、「配列やコレクション内の要素を並び替える」で詳しく説明しています。そちらでほぼ全て説明し終えているのですが、ここでは文字列の並び替えについて多少の補足をします。

デフォルトの並び替え方

文字列の配列(または、コレクション)をSortメソッドで並び替えたとき、デフォルトでは、現在のカルチャに依存して、大文字小文字を区別する方法で大小が比較され、並び替えられます。このことは、SortメソッドがCompareToメソッドを使って並び替えており、こちらで説明したように、String.CompareToメソッドが大文字小文字の区別をして、カルチャに依存する事実から明らかです。

注意:「String compare not always transitive | Microsoft Connect」によると、文字列にハイフンなどの記号が複数含まれている時、String.CompareToメソッドの結果がおかしくなることがあるようです。例えば、s1="-0.67:-0.33:0.33"、s2="0.67:-0.33:0.33"、s3="-0.67:0.33:-0.33"の時、これらをString.CompareToメソッドで比較すると、s1>s2、s2>s3なのに、s1<s3と判断されます。そのため、String.CompareToメソッドを使用しているSortメソッドも正しく並び替えできないことがあります。この問題は.NET Framework 4.0で修正されたようです(よって、.NET FrameworkのバージョンによってString.CompareToメソッドが違う値を返す可能性があります)。

大文字小文字を区別しない、カルチャに依存しないなど

大文字小文字を区別しない比較や、カルチャに依存しない比較を行いたい場合は、「配列やコレクション内の要素を並び替える」で紹介したような方法で可能です。しかしIComparerインターフェイスを実装した独自のクラスを作成するのは、面倒です。

幸いなことに、.NET Framework 2.0からは、文字列の並び替え用に、IComparerインターフェイスを実装したクラスがあらかじめ用意されています。これらのクラスはStringComparerクラスの派生クラスで、StringComparerクラスの静的プロパティから取得できます。現在は以下のような6つの静的プロパティが用意されています。

プロパティ名 説明(MSDN「StringComparer メンバ」より)
CurrentCulture 現在のカルチャの単語ベースの比較規則を使用して、大文字と小文字を区別して文字列を比較する StringComparer オブジェクトを取得します。
CurrentCultureIgnoreCase 現在のカルチャの単語ベースの比較規則を使用して、大文字と小文字を区別せずに文字列を比較する StringComparer オブジェクトを取得します。
InvariantCulture インバリアント カルチャの単語ベースの比較規則を使用して、大文字と小文字を区別して文字列を比較する StringComparer オブジェクトを取得します。
InvariantCultureIgnoreCase インバリアント カルチャの単語ベースの比較規則を使用して、大文字と小文字を区別せずに文字列を比較する StringComparer オブジェクトを取得します。
Ordinal 大文字と小文字を区別して序数の文字列比較を実行する StringComparer オブジェクトを取得します。
OrdinalIgnoreCase 大文字と小文字を区別せずに序数の文字列比較を実行する StringComparer オブジェクトを取得します。

序数の文字列比較とは、単純なバイト比較による方法のことです。カルチャに依存せず、言語的意味を持たない比較を行います。パフォーマンスに優れていますが、ユーザーにとって分かりにくい並べられ方になるかもしれません。

インバリアントカルチャとは、英語をベースとする、ロケールに依存しないカルチャのことです。インバリアントカルチャを使用するケースは限られています。例えば、どのカルチャでも使用できるリストを並び替えて永続化(保存)する時などです。一つの例を「配列やコレクション内に指定された要素があるか調べ、その位置を知る」で説明しています。

以下に、大文字小文字を区別せずに、序数比較で並び替える例を示します。

VB.NET
コードを隠すコードを選択
'並び替える配列を作成 
Dim strs As String() = New String() {"BBB", "ccc", "aaa"}

'大文字小文字を区別しない序数比較で並び替える 
Dim cmp As StringComparer = StringComparer.OrdinalIgnoreCase
Array.Sort(strs, cmp)

'並び替えた結果を表示する 
Console.WriteLine(String.Join(", ", strs))
'"aaa, BBB, ccc"と表示される 
C#
コードを隠すコードを選択
//並び替える配列を作成
string[] strs = new string[] { "BBB", "ccc", "aaa" };

//大文字小文字を区別しない序数比較で並び替える
StringComparer cmp = StringComparer.OrdinalIgnoreCase;
Array.Sort(strs, cmp);

//並び替えた結果を表示する
Console.WriteLine(string.Join(", ", strs));
//"aaa, BBB, ccc"と表示される

StringComparerのプロパティによりStringComparerオブジェクトを取得すると、そのたびにStringComparerオブジェクトが作成されます。よって、StringComparerの同じプロパティを何回も呼び出すことは無駄であり、変数に代入して使用すべきです。

指定したカルチャに依存した並び替えを行う

StringComparerクラスのCreateメソッドにより、指定したカルチャの規則を使った文字列比較を行うStringComparerオブジェクトを作成できます。また、2番目のパラメータで、大文字小文字を区別するかの指定もできます。

以下の例では、"ja-JP"カルチャに依存し、大文字小文字を区別しない並び替えを行っています。

VB.NET
コードを隠すコードを選択
'並び替える配列を作成 
Dim strs As String() = New String() {"BBB", "ccc", "aaa"}

'CultureInfoを作成 
Dim ci As New System.Globalization.CultureInfo("ja-JP")
'StringComparerを作成、大文字小文字を区別しない 
Dim cmp As StringComparer = StringComparer.Create(ci, True)

'並び替える 
Array.Sort(strs, cmp)

'並び替えた結果を表示する 
Console.WriteLine(String.Join(", ", strs))
'"aaa, BBB, ccc"と表示される 
C#
コードを隠すコードを選択
//並び替える配列を作成
string[] strs = new string[] { "BBB", "ccc", "aaa" };

//CultureInfoを作成
System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("ja-JP");
//StringComparerを作成、大文字小文字を区別しない
StringComparer cmp = StringComparer.Create(ci, true);

//並び替える
Array.Sort(strs, cmp);

//並び替えた結果を表示する
Console.WriteLine(string.Join(", ", strs));
//"aaa, BBB, ccc"と表示される

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

ひらがなとカタカナ、あるいは、全角と半角を区別しない並び替えを行いたいならば、こちらで説明したように、自分でIComparerインターフェイスを実装したクラスを作成します。文字列の比較は、「2つの文字列が等しいかを調べる」で紹介したように、CompareInfo.Compareメソッドを使えばよいでしょう。

以下にカルチャと比較オプションを指定して並び替えを行うことができるIComparerを実装したクラスの例を示します。このクラスではIComparerジェネリックインターフェイスも実装していますので、.NET Framework 2.0以上で使用できます。

VB.NET
コードを隠すコードを選択
''' <summary> 
''' カルチャと比較オプションを指定できるIComparer 
''' </summary> 
Public Class CultureInfoCompare
    Implements System.Collections.IComparer
    Implements System.Collections.Generic.IComparer(Of String)

    Private _compareInfo As System.Globalization.CultureInfo
    Private _compareOptions As System.Globalization.CompareOptions

    ''' <summary> 
    ''' CultureInfoCompareのコンストラクタ 
    ''' </summary> 
    ''' <param name="culture">並べ替えで使用するカルチャ</param> 
    ''' <param name="options">並べ替えで使用するオプション</param> 
    Public Sub New(ByVal culture As System.Globalization.CultureInfo, _
                   ByVal options As System.Globalization.CompareOptions)
        Me._compareInfo = culture
        Me._compareOptions = options
    End Sub

    Public Function Compare(ByVal x As String, ByVal y As String) As Integer _
        Implements System.Collections.Generic.IComparer(Of String).Compare

        Return Me._compareInfo.CompareInfo.Compare(x, y, Me._compareOptions)
    End Function

    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _
        Implements System.Collections.IComparer.Compare

        Return Me.Compare(x.ToString(), y.ToString())
    End Function
End Class
C#
コードを隠すコードを選択
/// <summary>
/// カルチャと比較オプションを指定できるIComparer
/// </summary>
public class CultureInfoCompare : System.Collections.IComparer,
    System.Collections.Generic.IComparer<string>
{
    private System.Globalization.CultureInfo _compareInfo;
    private System.Globalization.CompareOptions _compareOptions;

    /// <summary>
    /// CultureInfoCompareのコンストラクタ
    /// </summary>
    /// <param name="culture">並べ替えで使用するカルチャ</param>
    /// <param name="options">並べ替えで使用するオプション</param>
    public CultureInfoCompare(System.Globalization.CultureInfo culture,
        System.Globalization.CompareOptions options)
    {
        this._compareInfo = culture;
        this._compareOptions = options;
    }

    public int Compare(string x, string y)
    {
        return this._compareInfo.CompareInfo.Compare(x, y, this._compareOptions);
    }

    public int Compare(object x, object y)
    {
        return this.Compare(x.ToString(), y.ToString());
    }
}

このクラスを使ってひらがなとカタカナを区別しない並び替えを行う例を示します。

VB.NET
コードを隠すコードを選択
'並び替える配列を作成 
Dim strs As String() = New String() {"あああ", "ううう", "イイイ"}

'CultureInfoを作成 
Dim ci As New System.Globalization.CultureInfo("ja-JP")
'CultureInfoCompareを作成、ひらがなとカタカナを区別しない 
Dim cmp As New CultureInfoCompare(ci, _
    System.Globalization.CompareOptions.IgnoreKanaType)

'並び替える 
Array.Sort(strs, cmp)

'並び替えた結果を表示する 
Console.WriteLine(String.Join(", ", strs))
'"あああ, イイイ, ううう"と表示される 
C#
コードを隠すコードを選択
//並び替える配列を作成
string[] strs = new string[] { "あああ", "ううう", "イイイ" };

//CultureInfoを作成
System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("ja-JP");
//CultureInfoCompareを作成、ひらがなとカタカナを区別しない
CultureInfoCompare cmp =
    new CultureInfoCompare(ci, System.Globalization.CompareOptions.IgnoreKanaType);

//並び替える
Array.Sort(strs, cmp);

//並び替えた結果を表示する
Console.WriteLine(string.Join(", ", strs));
//"あああ, イイイ, ううう"と表示される
  • 履歴:
  • 2011/2/8 説明を追加。
  • 2013/10/28 String.CompareToメソッドの不具合に関する説明を追加。

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

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