■
数字混じり文字列ソート
で、なんで、Char.IsNumberについて見てたのかといえば、
というか、
が気になったから。
これ、どう見ても、正規表現を使い始めたら、.NETに勝ち目はない。Regex クラスの使い勝手の悪さに対処しているだけで精一杯。
そして、Linq的な処理を考えようにも、IEnumerable
結局、昔ながらの文字解析を行った方が順当っぽい。
ということで、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); }
うーん、パフォーマンスは出そうだけど、微妙なコードだなぁ。
Char.IsNumber/GetNumericValue
【VB.NET】指定した文字が数字かどうか判定する(IsDigit, IsNumber, IsNumeric)を見ていて、Char.GetNumericValueとint.Parse/TryParseに違いがあるのかどうかが気になったので試してみた。
using System; class NumericTest { public static void Main() { Console.OutputEncoding = System.Text.Encoding.UTF8; Char[] num = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '鄯', '鄱', '鄴', '?', '?', '?', '?', '?', '㈠', '㈡', '㈩', '⑴', '⒇', '❶', '❿', '➀', '➉', '㊀', '㊁', '㊉', '⒈', '⒛', '➊', '➓', '¹', '⁹', '₁', '₉', '〇', '一', '九', '零', '壱', '弐', '玖'}; foreach (Char c in num) { int v; if (!int.TryParse(c.ToString(), out v)) v = -1; Console.WriteLine( "{0}\tIsDigit:{1}\tIsNumber:{2}\tGetNumericValue:{3}\tParse:{4}", c, Char.IsDigit(c), Char.IsNumber(c), Char.GetNumericValue(c), v); } } }
はてながUnicode対応じゃないせいで、見にくいことになってるけど、本当は、
結論から言うと、おそらく、Char.IsDigitがtrueな場合には、int.Parseも動く。一方で、Char.IsNumberがtrueでも、int.Parseできるとは限らないが、Char.GetNumericValueは数値の値を返してくれるようだ。
ただ、じゃぁ、数値解析ルーチンは、Char.GetNumericValueを使えば良いかといえば、こいつは、漢数字とかローマ数字まで相手にしてしまうので、状況に応じて判断するしかないだろう。
全角の数字文字列を処理したいだけだったら、NFKD・NFKCあたりで正規化してからint.Parseするのが良いかも。
と思ったけど、やっぱり、Char.IsDigitでチェックして、int.Parseというのには問題があるようだ。
http://blogs.msdn.com/b/oldnewthing/archive/2004/03/09/86555.aspx
結局、
static bool isDigit(Char c) { return c >= '0' && c <= '9'; }
なんていうベタなのが順当らしい・・・。