c#にはIPAddressクラスがあります。プリニティブ型と同様にParse, TryParseメソッドが用意されています。しかし、これをIPv4のフォーマットの検証に使うと思わぬ落とし穴にはまることになります。
文字列を4つの1バイト数値と3つのピリオドで成る文字列かどうか判断したいときにTryParseを使うとします。以下式は変換が正常に完了します。
public IPAddress Validate(string ipString) // ipString="128.0.0.1" { IPAddress address; if(IPAddress.TryParse(ipString, out address)) { return null; } return address; }
しかし以下の式でも、予想に反して変換が完了してIPAddress型が取得できます。
public IPAddress Validate(string ipString) // ipString="65536" { IPAddress address; if(IPAddress.TryParse(ipString, out address)) { return null; } return address; // 0.0.255.255 と解釈される }
これはどういうことかというとMSDNのParseのページに書いてありますが、
静的 Parse メソッドは、ピリオド区切りの 10 進表記 (IPv4 の場合) またはコロン区切りの 16 進表記 (IPv6 の場合) で表された IP アドレスから IPAddress のインスタンスを作成します。
IP アドレスの構築方法は、ipString 内のピリオドで区切られた部分の数によって決まります。1 つの部分から成るアドレスは、ネットワーク アドレスに直接格納されます。クラス A のアドレスを指定する場合に便利な 2 つの部分から成るアドレスでは、先頭の部分がネットワーク アドレスの第 1 バイト、末尾の部分が右端の 3 バイトに配置されます。クラス B のアドレスを指定する場合に便利な 3 つの部分から成るアドレスでは、先頭の部分がネットワーク アドレスの第 1 バイト、2 番目の部分が第 2 バイト、最後の部分が右端の 2 バイトに配置されます。 たとえば、
部分の数と ipString の例 IPAddress の IPv4 アドレス 1 -- "65536" 0.0.255.255 2 -- "20.2" 20.0.0.2 2 -- "20.65535" 20.0.255.255 3 -- "128.1.2" 128.1.0.2
どうやら
- ピリオドが足りない場合先頭、後方に詰める。
- 1バイト以上は後ろから16進数に解釈される。
となるようです。
なので、ユーザーが入力した文字列のチェックにIPAddress.Parseを使うと全然使えないということになります。なので、"xxx.xxx.xxx.xxx"を厳密に期待している場合、正規表現と数値の範囲を行うしかりません。形式を厳密にチェックしようとするとこんな感じですかね?
public bool CheckIpString(string str) { string str = "255.255.255.256"; if (string.IsNullOrEmpty(str)) { throw new ArgumentException("str is null or empty."); } if (str.Length < 7 || str.Length > 15) { throw new FormatException("str is illegal fortmat (" + str + ")"); } Match m = Regex.Match(str, @"^(\d+)\.(\d+)\.(\d+)\.(\d+)$"); if (m.Success) { for(int i = 1; i < 5; i++) { if (!this..isInByteRange(m.Groups[i].Value)) { throw new FormatException("str is illegal fortmat (" + str + ")"); } } } return true; } // 0 ~ 255 の範囲内かどうかチェックする private static bool isInByteRange(string block) { byte result; return byte.TryParse(block, out result); }
相当めんどくさいですね。