文字列が数値かどうかを判別する方法というのは
実はPHPではちょっと面倒ではないかと思う。
PHPではその判別の方法として"is_int"と"is_numeric"の2種類がデフォルトで用意されています。
is_int : "文字列が数値かどうか"を判別する関数。ただしintではないstring、つまりGETやPOSTで与えられた文字列をぶちこむとたとえ数値のみの文字列でもFALSE。
is_numeric : "文字列が数値として利用できるか"を判別する関数。ただし1.23、-123などの小数点や負の数、果ては+0123.45e6 といった16進表記もTRUEになる。
つまりどちらも一長一短かつ足しても足りない。
PHPをやっていくなかでよくほしくなるのは、"文字列が数字だけで構成されているか(文字列が純粋に数値かどうか)"を判別する関数であり、上記の関数では目的にそぐいません。すわ正規表現の出番かとなるわけですが、でもこんな些細なことにわざわざ正規表現を使うのも重い(たぶん内部処理的に)。
これまでの日曜プログラミングでは必要になったら都度考えて個別に関数を作るなり条件分岐を作るなりで対応していたのですが、いい加減専用の自作関数を作ったほうがいいよねということで、作りました。
function Is_NumStr($String){
if(strval($String) === strval(abs(intval($String)))){
return TRUE;
}else{
return FALSE;
}
}
var_dump(Is_NumStr('123')); ← TRUE
var_dump(Is_NumStr('0123ABC'));
var_dump(Is_NumStr('123ABC'));
var_dump(Is_NumStr('123\0ABC'));
var_dump(Is_NumStr('ABC123'));
var_dump(Is_NumStr('000123'));
var_dump(Is_NumStr('0')); ← TRUE
var_dump(Is_NumStr('-123'));
var_dump(Is_NumStr('-12.3'));
var_dump(Is_NumStr('12.3'));
var_dump(Is_NumStr('0.1'));
var_dump(Is_NumStr('1.0'));
つまり、正規表現だと以下と(ほぼ)同義。
preg_match("/^[1-9]\d*$/",$String);
純粋に狭義(だけどぱっと思い浮かぶという意味ではデファクトスタンダード)の"数値"しか認めません。
果たしてどちらが処理にもたつくのか、試していないのでわかりませんが、これで正規表現バージョンより遅かったらどうしましょうね。なんとなくabsとか足を引っ張ってそうな気がしますが。
こっちのほうが早いのでは、ほかにもっと良い方法あるよ、というご意見があれば切実にお待ちしております。
----------------------------------------
[追記(1)]
しまった、これだと引数にTRUEを指定したとき[Is_NumStr(TRUE)]にTRUEだ……あと考えてみたら0のときの返り値をTRUEにしてもいいことは何もない気がする。
よって、
function Is_NumStr($String){
if($String && strval($String) === strval(abs(intval($String))) && !is_bool($String)){
return TRUE;
}else{
return FALSE;
}
}
やべぇ、俗っぽさがいっきにプンプンしてきたぜぇぇぇ!!
----------------------------------------
[追記(2)]
あと、
preg_match("/^[1-9]\d*$/",TRUE);
で1(TRUE)が返ってくるという現象を発見。これまで正規表現バージョン使ってなかったからいいんですが、実際使ってたらこれでマズいという状況もなくはないと思われますwww
----------------------------------------
[追記(3)]
var_dump(Is_NumStr(0123));
でOUT。
intval(0123)で83になるからですね……。16/8/2進数は今回求めたい"純粋な数値"じゃない可能性があるので排除したいところ。intval(0123,10)という具合に基数を指定しないと。
というわけで
function Is_NumStr($String){
if($String && strval($String) === strval(abs(intval($String,10))) && !is_bool($String)){
return TRUE;
}else{
return FALSE;
}
}
……しかしながら結果はダメ。
どうもintval()では基数を強制的に設定できないようです。早い話が、intval(0123)もintval(0123,10)も83。意味が、ない!!!!!!!! ※かといってエラーが返ってくるのも微妙だけど
ほか、試行錯誤しましたが、ムリっぽいです。
function Is_NumStr($String){
if(preg_match("/^[1-9]\d*$/",$String) && !is_bool($String)){
return TRUE;
}else{
return FALSE;
}
}
という正規表現+みたいなのが一番いいのかも……という結論に……。ただまぁ、(int) 0123みたいな引数を条件分岐に使うこともないと思いますので、追記(1)のものでも実用には耐えうる……かな? やめたほうが無難ですね……。
うーん。
----------------------------------------
[追記(4)]
うっかりしてましたが、intで0123という数値を変数に代入した時点(=引数として設定した時点)でこれはもう10進数の数値ではないので、追記(3)の正規表現バージョンでもどっちみちムリ。
やるとするなら関数の最初にis_numericで数値かどうかを確認、その後、頭の文字で基数を割り出す。10進数でなければFALSE。としなければいけません。ん、基数ってどうやって求めるんだ? base_convertをかけてみて元の数字と比較するとか? ん、できるのかな? んんん。
そこまですることかどうか、もうわからなくなってきたので、追記(1)のバージョンが使えますね良かった良かったということで良いのではないでしょうか。
3連休はずっとこんなことを考えていたので、いい加減作業に戻ります。だから遅れるんだよVer.3.0も同人部も!!
今回の教訓 : 基本を覚えてから先に進め(»PHPをいじりだした頃から今に至るまでの自分)
----------------------------------------
[追記(5)]
そしてまさかのctype_digit関数。
2010年03月11日 0時更新
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
|---|---|---|---|---|---|---|
| « 10月 | - | 12月 » | ||||
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | |||||
