为什么 Char.IsDigit 对于无法解析为 int 的字符返回 true?



我经常使用Char.IsDigit来检查char是否是数字,这在LINQ查询中特别方便,可以预检查int.Parse,如下所示:"123".All(Char.IsDigit)

但也有一些字符是数字,但不能像۵那样解析为int

// true
bool isDigit = Char.IsDigit('۵'); 
var cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
int num;
// false
bool isIntForAnyCulture = cultures
.Any(c => int.TryParse('۵'.ToString(), NumberStyles.Any, c, out num)); 

为什么?我的int.Parse-通过Char.IsDigit预检查是否因此而不正确?

有310个字符是数字:

List<char> digitList = Enumerable.Range(0, UInt16.MaxValue)
.Select(i => Convert.ToChar(i))
.Where(c => Char.IsDigit(c))
.ToList(); 

以下是Char.IsDigit在.NET4(ILSpy)中的实现:

public static bool IsDigit(char c)
{
if (char.IsLatin1(c))
{
return c >= '0' && c <= '9';
}
return CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber;
}

那么,为什么有属于DecimalDigitNumber类别的字符("十进制数字字符,即0到9之间的字符…">)在任何区域性中都无法解析为int呢?

这是因为它正在检查Unicode"数字,十进制数字"类别中的所有数字,如下所示:

http://www.fileformat.info/info/unicode/category/Nd/list.htm

这并不意味着它在当前区域设置中是一个有效的数字字符。事实上,使用int.Parse(),无论语言环境设置如何,都只能解析正常的英文数字。

例如,这个不起作用:

int test = int.Parse("٣", CultureInfo.GetCultureInfo("ar"));

尽管٣是一个有效的阿拉伯数字字符,并且"ar"是阿拉伯语言环境标识符。

微软的文章"如何:解析Unicode数字"指出:

.NET Framework解析为小数的唯一Unicode数字是ASCII数字0到9,由代码值U+0030到U+0039指定。.NET Framework将所有其他Unicode数字解析为字符。

但是,请注意,您可以使用char.GetNumericValue()将unicode数字字符转换为等效于双精度的数字字符。

返回值是double而不是int的原因是这样的:

Console.WriteLine(char.GetNumericValue('¼')); // Prints 0.25

您可以使用类似的方法将字符串中的所有数字字符转换为ASCII等效字符:

public string ConvertNumericChars(string input)
{
StringBuilder output = new StringBuilder();
foreach (char ch in input)
{
if (char.IsDigit(ch))
{
double value = char.GetNumericValue(ch);
if ((value >= 0) && (value <= 9) && (value == (int)value))
{
output.Append((char)('0'+(int)value));
continue;
}
}
output.Append(ch);
}
return output.ToString();
}

十进制数字是0到9,但它们在Unicode中有许多表示形式。来自维基百科:

十进制数字在23个单独的块中重复

MSDN指定.NET只解析拉丁数字:

然而,解析方法识别的唯一数字是基本拉丁数字0-9,代码点从U+0030到U+0039

最新更新