Warning: sqlite_open() [function.sqlite-open]: file is encrypted or is not a database in /virtual/php/public_html/www.trash-news.net/php/include/CommonFunction.php on line 83

Warning: sqlite_query() expects parameter 1 to be resource, string given in /virtual/php/public_html/www.trash-news.net/php/include/CommonFunction.php on line 85

TRASH-NEWS 2009年11月25日の記事一覧

順路
  1. ホーム
  2. 2009年
  3. 11月
  4. 25日 [現在地]
Search?
お知らせ
社会人近況 (2008/09/07 0:00)
配属されてからこの方、ほぼ毎日終電での帰宅が続いているうえに毎週泊まりがけの出張が入っているため、1週間に1回か2回更新できればマシといった状況です。実はまだTRASH-NEWSは概要ページもリンクコーナーもろくに作っていないような有様なんですけれども、気づけばそろそろ1周年+すでに50万ヒット。……なんだこのえもいわれぬグダグダ感は……。とりあえずヴェスペリア堪能してから考えますね。
1 2 3

2009年11月25日

2009年11月25日のニュースとネタをお届けします

関連した日付へのリンク
  1. 2009年の記事一覧
  2. 2009年11月の記事一覧
TRASH-NEWSは
I've Soundをこよなく愛するサイトです

2009年11月25日本日のニュースとネタ

PHPで文字列が(純粋に)数値かどうかを判別する方法、の続きです。

上記の記事冒頭。
× PHPではその判別の方法として"is_int"と"is_numeric"の2種類がデフォルトで用意されています。
PHPではその判別の方法として"is_int"と"is_numeric"、"ctype_digit"の3種類がデフォルトで用意されています。

ctype_digit : "文字列が10進数字か"を判別する関数。ただしstringを指定したときのみ正常に動き、intを指定すると常にFALSEという利用者軽視の困った仕様。

ctype 関数は、正規表現よりもつねに好ましく、さらに str_* および is_* のような いくつかの等価な関数よりも好ましいことに注意してください。 これは、ctype 関数がネーティブな C ライブラリを使用しており、処理が著しく 高速であるためです。 (PHP : ctype関数)

とのこと by PHPの中の人。

ひょっとしてctype_digitを使えばよいのではないかというすごくいまさらなアイディアが脳内を。このctype_digit関数、歴史的に残念なあやまちはあるものの、C言語的な意味でネイティブな処理ならこれに勝る挙動はありません。is_*関数より好ましいとか名指しで書かれちゃってますしね!

ひとつネックである『int指定時に常にFALSE』という仕様も、is_intと組み合わせれば良いわけで、これで隙はなくなったなァァァ!!!!

と……でもこれって、文字列の端から桁の概念ナシに1文字ずつ数字かどうか確認していくみたいですから"01234"でもTRUEなんですよね……。たとえばformでユーザーから数値の入力を求めたときに01234なんて文字列が送られてきたら、これは『問題はないけど間違ってはいる』レベルであり、個人的にはエラーを返したい。でもctype_digit+is_intじゃダメ。数字は数字ということでOKになってしまう。いやだから数字かどうかではなく、数値かどうかを知りたいのに。ということはやはり『1文字目が0だとFALSEを返す』あるいは『数値化したとき元の文字列とstringとして比較して異なるとFALSEを返す(ややこしい)』仕様がほしい。と、なると……ああ、やっぱり正規表現か先述の記事で示した以下のコードが有用ということに。


function Is_NumStr($String){
	if($String && strval($String) === strval(abs(intval($String))) && !is_bool($String)){
		return TRUE;
	}else{
		return FALSE;
	}
}

結局こんな俗っぽい処理が一番なのかなぁ。うーん。うーん……。

----------------------------------------

[追記(1)]
そういえば上記の方法だとintvalの仕様上、システム依存で扱える桁数に制限がかかるというのが問題。だとすると正規表現か、ctype_digitに軍配があがるわけで……まぁでもintの範囲を超えるような数を(たとえ文字列だとしても)扱うことはないし、いいのかな。いや……そこで妥協するくらいならおとなしく正規表現でいいじゃないかという……でもそれだと何かに負けた感じが……ゴゴゴゴゴ。


// [Release ver.1.01]
function Is_NumStr($String){
	if(substr($String,0,1) && (intval($String) > 0) && (is_int($String) or ctype_digit($String))){
		return TRUE;
	}else{
		return FALSE;
	}
}

これでどうだ。substrで頭の1文字が0でないことを確認、intvalと数値比較で負の数でないことを確認、その後の2つの関数でint/stringを問わず数字かどうかを判別。これでようやく、欲していた『文字列が(純粋に)数値かどうか=文字列の見た目が正の整数かどうかをint/stringを問わず判別する』関数が出来上がり。ああややこしかった。

var_dump(Is_NumStr('123')); // TRUE
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));
var_dump(Is_NumStr('0'));
var_dump(Is_NumStr('-123'));
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(0.3));
var_dump(Is_NumStr(.3));
var_dump(Is_NumStr('1.0'));
var_dump(Is_NumStr(''));
var_dump(Is_NumStr(NULL));
var_dump(Is_NumStr(TRUE));
var_dump(Is_NumStr(FALSE));
var_dump(Is_NumStr('123123123123123123123123123123123123123')); // TRUE
var_dump(Is_NumStr(0123)); // TRUE

確認結果もOK。求める仕様は全部満たしました。うん、(たぶん)(もうこれで)いい。なんだかえらくスマートというか、すごく単純すぎる結果に落ち着いてしまってこれもこれで負けた気がする。

----------------------------------------

[追記(2)]
こっそり追記。(intval($String) > 0)の部分で負の数かどうかを判別するように。これがないとIs_NumStr(-123)でTRUEが返ります。追記に伴ってRelease ver.も1.01へアップ。

----------------------------------------

[追記(3)]
var_dump(Is_NumStr(123123123123123123123123123123123123123123123123));
でアウト。考えてみればintvalでダメ。is_intでもそういえばダメ。もうこうなったらdoubleなfloatは関数の頭でハナから弾くとかしないとだめかもね。前記事で触れた10進数以外の数字問題もそうだけど、僕の求めている仕様を完全に満たすにはやっぱりシンプルな書き方だけじゃダメみたい。結局こういうオチか。そもそも非常識な数値をこの関数の引数に指定する機会はあるはずないと言い聞かせるしかなさそうです。

----------------------------------------

[追記(4)]
この記事を書いたのが夜の3時過ぎで、ああやべぇ会社早いしもう寝なきゃなと思いつつ↑の追記(3)を書いたのですが、布団に入ってよくよく考えてみると以下の処理で済むんじゃないかと気づいたんですよ。


// [Release ver.1.1]
function Is_NumStr($String){
	if(!is_bool($String) && substr($String,0,1) && ctype_digit(strval($String))){
		return TRUE;
	}else{
		return FALSE;
	}
}

いま家に帰って確かめてみたら案の定イケました。というか最初から思いついておけよというくらい単純な方法でした。青い鳥ってやつですね。

ちなみに上記のver.1.1の方法でも、preg_match("/^[1-9]\d*$/",$String)といった正規表現でも、doubleのfloatは扱えないようです。strval(123123123123123123123)でそもそも指数表示になるんですね。'123123123123123123123'にはならないみたい。はじめて……知った……。独学者特有の『初歩的なところでのほころび』がこんなところで露呈するとは。

ついでに備忘録として書いておくと、strval(TRUE)は1になる。まぁたしかに1になってくれないと困るんだけど、ちょっと微妙。

[ 関連キーワード : (キーワードは登録されていません) ]

文字列が数値かどうかを判別する方法というのは
実は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関数

[ 関連キーワード : (キーワードは登録されていません) ]

サイト内検索
検索する
このサイトについて
  • サイト名 : TRASH-NEWS
  • 分類 : ニュースとネタのサイト
  • ジャンル : 井戸端からアレゲまで
  • もっと詳しく見る
この日の人気記事ランキング
2009年11月25日のぶん
1
俗・PHPで文字列が(純粋に)数値かどうかを判別する方法
31
2
PHPで文字列が(純粋に)数値かどうかを判別する方法
28
3
Windows XP SP3インストールで不具合が起きることが発覚
23
4
もうやめて下さい!! 泣いてる子もいるんですよ!!
15
5
テイルズオブヴェスペリアの感想 【ストーリー・キャラクター編】
14
6
ついうっかりマリオWiiとグレイセスとFFXIIIとDQVIを予約(以下略)
13
7
マリオっぽい自作のゲーム改めしょぼんのアクション公開開始
12
8
PSP版スターオーシャン2、新キャラにやはりウェルチ
11
9
画像解析系ジェネレータ・BannerCodeBattler2(仮)着手開始
9
10
「テイルズ オブ」シリーズが全世界で売り上げ1,000万本突破
9

2009年11月26日 0時更新

人気記事ランキング

カテゴリ(折畳表示)
ニュース・ネタ
サブカテゴリを開く
俺研究舎
サブカテゴリを開く
お知らせ
サブカテゴリを開く
特集・企画
サブカテゴリを開く
管理人近況
サブカテゴリを開く
カレンダー
2009年11月
«  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          
管理人について
管理人 : 八満かシアン
理系になり損ねた文系人間(シアンのほう)
今頃たぶん聴いている曲
ナイショ☆Naiしょ by 詩月カオリ
月別アーカイブ
2009年
2008年
2007年
ユーティリティ
RSSフィード
ソーシャルブックマーク
  • iGoogle
  • はてなアンテナにTRASH-NEWSを追加する TRASH-NEWSのはてなブックマーク数 TRASH-NEWSを含むはてなブックマーク
サイトプロフィール
Powered by
  • Movable Type 4.1 + [XHTML + CSS + JavaScript] + PHP
  • TRASH-NEWS / Hachiman_Cian 2007-
このページについて
題名
2009年11月25日の記事一覧
内容
  • 2009年11月25日のニュースとネタをお届けします
TRASH-NEWS ロゴ
TRASH-NEWS ロゴ
TRASH-NEWS : http://www.trash-news.net/