char.IsDigit() と 正規表現の"\d" は、半角数字にマッチする。
が、それだけでなく、UNICODEカテゴリ"Nd"(Number, Decimal Digit) にもマッチする。
上記を意識して使わないと、高確率でバグを仕込んでしまう。
例えば、日本語システムだと全角数字("0" ~ "9")の入力が想定できるため、半角数字と区別する必要がある場合に、これらを使用するとバグる。
他に、これらを使ってチェックし、int.Parse() に投げるなども NG。
※普通は、int.TryParse() 推奨
[結果]
TAI THAM HORA DIGIT (1A80~9)、TAI THAM THAM DIGIT (1A90~9)、JAVANESE DIGIT (A9D0~9)、MEETEI MAYEK DIGIT (ABF0~9) が数字文字として判定されず、UNICODEカテゴリが"Cn"(Other, Not Assigned) となった。
原因は、判定されなかった文字の UNICODE バージョンは 5.2.0 で、Windows7/.NET 4.0 の UNICODE バージョンは 5.1 だからである。(参考:.NET の UNICODE バージョン)
環境がないので検証できないが、Windows 8/.NET 4.5 なら数字文字として判定されるはず。
正規表現の場合
が、それだけでなく、UNICODEカテゴリ"Nd"(Number, Decimal Digit) にもマッチする。
上記を意識して使わないと、高確率でバグを仕込んでしまう。
例えば、日本語システムだと全角数字("0" ~ "9")の入力が想定できるため、半角数字と区別する必要がある場合に、これらを使用するとバグる。
他に、これらを使ってチェックし、int.Parse() に投げるなども NG。
※普通は、int.TryParse() 推奨
UNICODEカテゴリ"Nd"にマッチすることの検証
[検証コード]
using System;
using System.Globalization;
using System.Text.RegularExpressions;
static class Program
{
static void Main()
{
foreach( var point in DigitZeroPoints ){
for( var c = point; c < point + 10; c++ ){
var char_isdigit = char.IsDigit(c);
var regex_isdigit = Regex.IsMatch(c.ToString(), @"\d");
if( !char_isdigit || !regex_isdigit ){
// どちらかのマッチ失敗時のみコンソール出力
Console.WriteLine(
"{0:X4} - char={1} regex={2} category={3}",
(int)c, char_isdigit, regex_isdigit,
CharUnicodeInfo.GetUnicodeCategory(c));
}
}
}
}
// UNICODEカテゴリ"Nd"の ZERO のコードポイント一覧
// ※FileFormat.Info から 2014/03/26 に取得
// ※4Byte 文字は char で表現できないため、除外
private static readonly char[] DigitZeroPoints = new[] {
'\u0030', // DIGIT ZERO
'\u0660', // ARABIC-INDIC DIGIT ZERO
'\u06F0', // EXTENDED ARABIC-INDIC DIGIT ZERO
'\u07C0', // NKO DIGIT ZERO
'\u0966', // DEVANAGARI DIGIT ZERO
'\u09E6', // BENGALI DIGIT ZERO
'\u0A66', // GURMUKHI DIGIT ZERO
'\u0AE6', // GUJARATI DIGIT ZERO
'\u0B66', // ORIYA DIGIT ZERO
'\u0BE6', // TAMIL DIGIT ZERO
'\u0C66', // TELUGU DIGIT ZERO
'\u0CE6', // KANNADA DIGIT ZERO
'\u0D66', // MALAYALAM DIGIT ZERO
'\u0E50', // THAI DIGIT ZERO
'\u0ED0', // LAO DIGIT ZERO
'\u0F20', // TIBETAN DIGIT ZERO
'\u1040', // MYANMAR DIGIT ZERO
'\u1090', // MYANMAR SHAN DIGIT ZERO
'\u17E0', // KHMER DIGIT ZERO
'\u1810', // MONGOLIAN DIGIT ZERO
'\u1946', // LIMBU DIGIT ZERO
'\u19D0', // NEW TAI LUE DIGIT ZERO
'\u1A80', // TAI THAM HORA DIGIT ZERO
'\u1A90', // TAI THAM THAM DIGIT ZERO
'\u1B50', // BALINESE DIGIT ZERO
'\u1BB0', // SUNDANESE DIGIT ZERO
'\u1C40', // LEPCHA DIGIT ZERO
'\u1C50', // OL CHIKI DIGIT ZERO
'\uA620', // VAI DIGIT ZERO
'\uA8D0', // SAURASHTRA DIGIT ZERO
'\uA900', // KAYAH LI DIGIT ZERO
'\uA9D0', // JAVANESE DIGIT ZERO
'\uAA50', // CHAM DIGIT ZERO
'\uABF0', // MEETEI MAYEK DIGIT ZERO
'\uFF10', // FULLWIDTH DIGIT ZERO
};
}
[結果]
1A80 - char=False regex=False category=OtherNotAssigned : 1A89 - char=False regex=False category=OtherNotAssigned 1A90 - char=False regex=False category=OtherNotAssigned : 1A99 - char=False regex=False category=OtherNotAssigned A9D0 - char=False regex=False category=OtherNotAssigned : A9D9 - char=False regex=False category=OtherNotAssigned ABF0 - char=False regex=False category=OtherNotAssigned : ABF9 - char=False regex=False category=OtherNotAssigned
TAI THAM HORA DIGIT (1A80~9)、TAI THAM THAM DIGIT (1A90~9)、JAVANESE DIGIT (A9D0~9)、MEETEI MAYEK DIGIT (ABF0~9) が数字文字として判定されず、UNICODEカテゴリが"Cn"(Other, Not Assigned) となった。
原因は、判定されなかった文字の UNICODE バージョンは 5.2.0 で、Windows7/.NET 4.0 の UNICODE バージョンは 5.1 だからである。(参考:.NET の UNICODE バージョン)
環境がないので検証できないが、Windows 8/.NET 4.5 なら数字文字として判定されるはず。
おまけ
半角数字のみを判定したい場合は、下記を使っている。
public static bool IsAsciiDigit(this char c)
{
return '0' <= c && c <= '9';
}
正規表現の場合
// \d を使わず素直に書く
var regex_isdigit = Regex.IsMatch(c.ToString(), "[0-9]");
// または
// ECMAScript 準拠の正規表現ならば、半角数字のみにマッチする
var regex_isdigit = Regex.IsMatch(c.ToString(), @"\d", RegexOptions.ECMAScript);
0 件のコメント:
コメントを投稿