トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

C#/数値を考慮して並べ替えるIComparer

一部の情報は非常に古いもの(20年以上前〜)ですので、利用する際はご注意ください(Java 1.4 とか .NET 1.0 とか、Windows 2000 とか)
お問い合せは wiki@shise.net まで。Gmail に転送されるので、スパムは全部カットされます。


 

概要

文字列の並べ替えをする IComparer です。
普通のとちょっと違うのは、数字を考慮して並べ替えます。
たとえば、

a10.txt
a20.txt
a1.txt
a2.txt

これを普通に並び替えると、

a1.txt
a10.txt
a2.txt
a20.txt

となります。
これは、文字列として単純に比較しているからです。

ですが、ここの IComparer を使うと、

a1.txt
a2.txt
a10.txt
a20.txt

といった具合に、数字を考慮して並べ替えてくれます。

コード

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace StringRegulerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // テスト
            string[] s = new string[]{
                "hoge6.txt",
                "hoge7.txt",
                "hoge501.txt",
                "hoge10.txt",
                "hoge22.txt",
                "hoge1.txt",
                "hoge0001.txt",
                "hoge2.txt",
                "hoge3.txt",
                "test",
                "test_null",
                "i386.cat",
                "test1",
                "test01",
                "test002",
                "test00x2",
                "i0385.aaa",
                "i386.dat",
            };

            Array.Sort(s, new StringComparer());
            foreach (string var in s)
            {
                Console.WriteLine(var);
            }
        }
    }

    public class StringComparer : IComparer
    {
        public static bool NumCheck = true;

        private static string _numRegex = @"^(.*?)([0-9]+).*?$";
        private static Regex regex = new Regex(_numRegex);

        //xがyより小さいときはマイナスの数、大きいときはプラスの数、
        //同じときは0を返す
        public int Compare(object x, object y)
        {
            string a = Convert.ToString(x);
            string b = Convert.ToString(y);

            string aorg = a;
            string borg = b;

            // 何もしなくても等しかったら0
            if (a == b)
            {
                return 0;
            }

            // 数字部分切り出し保存用
            int? ai = null;
            int? bi = null;

            // 数字チェックするなら
            if (NumCheck)
            {
                // 正規表現で切り出す
                Match matchCol = regex.Match(a);

                // マッチしたら
                if (matchCol.Success)
                {
                    // 数字の前までの文字列と
                    a = matchCol.Groups[1].Value;
                    // 数字に分ける
                    ai = Convert.ToInt32(matchCol.Groups[2].Value);
                }

                // 正規表現
                matchCol = regex.Match(b);
                // マッチ
                if (matchCol.Success)
                {
                    // 文字列
                    b = matchCol.Groups[1].Value;
                    // 数字
                    bi = Convert.ToInt32(matchCol.Groups[2].Value);
                }
            }

            // 文字列の比較
            int t = string.Compare(a, b);

            // 等しければ
            if (NumCheck && t == 0)
            {
                // 
                if (ai == null && bi != null)
                {
                    t = -1;
                }
                else if (ai != null && bi == null)
                {
                    t = 1;
                }
                else if (ai == null && bi == null)
                {
                    t = string.Compare(aorg, borg);
                }
                else
                {
                    t = (int)(ai - bi);
                    if (t == 0)
                    {
                        t = string.Compare(aorg, borg);
                    }
                }
            }

            return t;
        }
    }
}