読者です 読者をやめる 読者になる 読者になる

数字混じり文字列ソート

で、なんで、Char.IsNumberについて見てたのかといえば、

数字混じり文字列ソート - どう書く?org

というか、

数字混じり文字列ソート ~ Rubyとの血みどろの闘い ~

が気になったから。
これ、どう見ても、正規表現を使い始めたら、.NETに勝ち目はない。Regex クラスの使い勝手の悪さに対処しているだけで精一杯。
そして、Linq的な処理を考えようにも、IEnumerable.Compareみたいなのがあれば良いんだけど、そういうのもないし。Enumerable.SequenceEqualは等値のチェックしか出来ない。あったところで、その後が続かない。

結局、昔ながらの文字解析を行った方が順当っぽい。
ということで、Linqスタイルとか、そういうのを全部無視して、素直にArray.Sortに渡す比較関数を考えてみることに。

static bool isDigit(Char c)
{
  return c >= '0' && c <= '9';
}

static int extractNumber(String a, ref int pos)
{
  int end = pos;
  while (end < a.Length && isDigit(a[end]))
    end++;
  int ret = int.Parse(a.Substring(pos, end - pos));
  pos = end;
  return ret;
}

static int compare(String a, String b)
{
  int iA = 0, iB = 0;
  int lenA = a.Length;
  int lenB = b.Length;
  while (iA < lenA && iB < lenB)
  {
    Char cA = a[iA], cB = b[iB];
    if (!isDigit(cA) || !isDigit(cB))
    {
      int d = (int)cA - (int)cB;
      if (d != 0)
        return d;
    }
    else
    {
      int vA = extractNumber(a, ref iA);
      int vB = extractNumber(b, ref iB);
      int d = vA - vB;
      if (d != 0)
        return d;
    }
    iA++;
    iB++;
  }
  return (lenA - iA) - (lenB - iB);
}

うーん、パフォーマンスは出そうだけど、微妙なコードだなぁ。