ファイル名のソートについて
C#でファイル名を列挙するには、System.IO.Directory.GetFiles()メソッドを使用する。
ただしこのメソッドで得られるファイル名の順番は保証されていない。
このことはMSDNのGetFilesメソッドの説明 にも書かれている。
そんな訳で、ファイル名の順序に意味がある場合は並べ替えをする必要があるのだが、ファイル名によっては文字列ソートだと思ったとおりに並ばない。
例えば、"file1.txt", "file2.txt", "file3.txt", ..., "file10.txt"のような文字列 + 通し番号の様な名前の場合、文字列ソートだと、
file1.txt
file10.txt
file2.txt
のようになってしまう。
これを数字順に並べる方法を紹介する。
shlwapi.dllのStrCmpLogicalWを利用すれば簡単に並べ替えができる。
StrCmpLogicalWについてはこちら を参照。
-- C# --
using System.Runtime.InteropServices;
public class FileNameComparer : IComparer<string>
{
// 並び順切り替えの為の変数。 1:昇順、-1:降順
private readonly int modifier = 1;
public FileNameComparer()
: this(false)
{ }
public FileNameComparer(bool descending)
{
if (descending) modifier = -1;
}
public int Compare(string a, string b)
{
return NativeMethods.StrCmpLogicalW(a ?? "", b ?? "") * modifier;
}
internal static class NativeMethods
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
}
}
実際に文字列を比較する関数はNativeMethodsクラスで用意している。
インポートするのは、shlwapi.dllのStrCmpLogicalW関数。
インポート時にCharSetをUnicodeを指定している。
IComparer<T>を実装した比較用のクラスを作成する。
ソートの昇順、降順はインスタンス作成時に設定するようにする。
インスタンス作成時にtrueを渡せば降順、falseを渡せば昇順になる。
あとはCompareメソッドで2つの文字列を比較した結果を返す。
実際の比較はStrCmpLogicalW関数が行うので、後は変数modifierを使ってStrCmpLogicalWの戻り値を補正する。
??演算子は左オペランドがnullだった場合は右オペランドを返す。
nullじゃなかったら左オペランドを返す。
これで、文字列がnullだった場合に備えている。
では、以下のコードを実行して試してみる。
-- テスト用コード --
string[] files = { "file1", "file2", "file3", "file10", "file11", "file20", };
Console.WriteLine("通常のソート");
Array.Sort(files);
foreach (string file in files)
{
Console.WriteLine(file);
}
Console.WriteLine("\nファイル名ソート:昇順");
Array.Sort(files, new FileNameComparer());
foreach (string file in files)
{
Console.WriteLine(file);
}
Console.WriteLine("\nファイル名ソート:降順");
Array.Sort(files, new FileNameComparer(true));
foreach (string file in files)
{
Console.WriteLine(file);
}
自分で1から作ると結構大変そうだけど、StrCmpLogicalWを利用することで簡単に並べ替え出来る。
コメント