▼ 2008/05/15(木) 日本語文字列のバイト数取得にstrlenだけではダメな理由-PHP
日本語(マルチバイト)の処理において、その文字数ではなく、バイト数を取得したい場合があります。
マルチバイトは必ずしも1文字2バイトとは限らず、EUC-JPでは概ね2バイトだけど特殊文字で3バイトの場合があり、UTF-8では概ね3文字バイトで表されるけれども、そうでない場合もある。
マルチバイト文字はエスケープシーケンス等様々な理由で、日本語の文字数×nでは答えがでないのです。
もっと単純な話でいえば、シングルバイト文字とマルチバイト文字が混ざった文字列のバイト数は、もちろん文字数×nでは求めることができません。
マルチバイトは必ずしも1文字2バイトとは限らず、EUC-JPでは概ね2バイトだけど特殊文字で3バイトの場合があり、UTF-8では概ね3文字バイトで表されるけれども、そうでない場合もある。
マルチバイト文字はエスケープシーケンス等様々な理由で、日本語の文字数×nでは答えがでないのです。
もっと単純な話でいえば、シングルバイト文字とマルチバイト文字が混ざった文字列のバイト数は、もちろん文字数×nでは求めることができません。
■まず現状での最善策をご紹介
$volm = strlen(bin2hex($data)) / 2; $data:バイト数を取得したいデータ $volm:データ長(byte)これは私のオリジナルではありません。Script雑感-php:バイト数の取得(strlen は mb_strlen にオーバーロードされる)様の引用です。
Qdmailの修正中に、どんな環境でも問題なく、日本語(マルチバイト文字)のバイト数を取得する必要がでてきてきたのですが、どうしてもスマートな方法を考えつきませんでした。
そこに、Script雑感さんのページに遭遇しました。たいへん助かりました。
参考
$str_len_byte = mb_ereg('.*', $string, $array);
という方法もあるようです。[pgsql-jp: 34062 データサイズの取得方法 ]しかし正規表現は速度的に不利なのとスマートでないのとmb_*環境がない場合には使えないので、bin2hex関数をお薦めします。■なぜ、strlen単独ではダメなのか?
オーバーロード
インターネットで検索するといくつか候補がみつかるのですが、その多くは次のようなものです。
(環境によって誤動作する例)しかし、これでは誤動作することがあるのです。
$volm = strlen($data);
$data:バイト数を取得したいデータ
$volm:データ長(byte)
PHPでは、php.iniの設定によっては、勝手に strlen が mb_strlen としてオーバーロードされます。
つまり、strlenを使っているつもりで、いつのまにか、mb_strlenを使っていることがあるのです。
関数のオーバーロード機能-PHPマニュアル
したがって、次のような事態になることもあります。
バイト数を数えたかったのに、マルチバイトの文字数を数えてしまった。これでは意図するプログラミングができません。
php.iniの設定を変えればいい話なのか?
関数のオーバーロード機能-PHPマニュアルにあるように、php.iniを変更すれば、これらの意図しない動作を押さえることができます。しかし、このphp.iniのmbstring.func_overloadの値を変えると、他のスクリプトへの影響がでる可能性があり、別の誤動作を生む可能性があります。
また、set_ini では、このディレクティブを変更することはできない仕様になっています*1ので、スクリプト内で一時的にmbstring.func_overloadをゼロにする対応も難しいです。。
このディレクティブを変更できるかどうかもphp.iniで変更できないことはないのですが、話がややこしくなり、お薦めできません。
したがって、php.iniでの対応は、あまり望ましくないといえます。
また、「自分の環境では、mbstring.func_overloadはゼロだから、別にいいや」という考え方もできますが、そのスクリプトを別のサーバーに移動させたときなどに、異常がでる可能性があります。
bin2hex関数の働き
bin2hexは、シングルバイト文字を16進数表記に変換する関数です。16進数表記の場合、シングルバイト文字1文字は必ず2文字の16進数に変換されます*2。したがって、その16進数表記の半分が、当該バイト数というわけですね。
▼ コメント(0件)
- TB-URL http://www.cpa-lab.com/tech/0144/tb/
-
▼
CakePHP(RC1.2)のemailコンポーネントで日本語はまだ厳しい
CPA-LABテクニカル ■CakePHP(1.2RC2)のemailコンポーネント評価CakePHP 1.2 RC2 がリリースされたので、新しいemailコンポーネントを評価してみた。全般的に言えば、改行コード&英文周りはかなり改善されており、英語メールを送る分には、大丈夫かと...
▼ 2008/05/15(木) CakePHPコントローラーをメール受信をきっかけに起動する
■CakePHPシェル機能を使用せずに、空メールを実現する-さくらインターネット編
CakePHPにはシェル機能というものがあり、コマンドラインからCakePHPを利用できる。この機能を利用して、CakePHPをメール受信をきっかけにリアルタイム処理することもできる。
例えばこちらの記事はそう。Writing Some Code-メール受信からのシェル機能実行
しかし、学習の量は少なければ少ないほど効率がいい。
あることを実現するのに、別の事柄を学ぶよりも、これまでの延長線上で可能な方がいい。
(ただし、WEBブラウザから直接叩かれることも想定されるのでご注意を 参考:CakePHPシェルの使い方)
というわけでここでは、CakePHPのシェル機能は使わずに、普通のControllerを、メール受信をきっかけに起動することをやってみたい。
1.2betaで動作確認をしていますが、1.1でも動作するはずです。
これはさくらインターネットで試していますが、".mailfilter"の部分さえ、ご自分の環境に合わせて設定できれば、後は同じです。さくらインターネットと同じ環境ではない人は、こちらの阿部辰也のブログ――人生はひまつぶし。-メール受信時に perl スクリプトを起動して自動処理させる方法が参考になるでしょう*1。
.mailfilterの設定
まず、.mailfilterに以下のように記述する。mailfilterについては、さくらインターネットでメール受信をリアルタイムに処理する、を参照。
ここでは、receivers_contoroller.php に記述された ReceiversContorollerを起動することを意図してみよう。
cc "| /usr/local/php-5.2.6/bin/php-cgi -q /home/user_name/hogehoge/app/webroot/index.php url=receivers/index"サーバーにメールを残す必要がない場合は、cc を to に変更する。
注意点 パラメーターの渡し方
urlのパラメーターは、次のように'?'を使用して記述した場合には失敗する。index.php?url=receivers/index次のように、index.phpとurl=の間には、半角空白を挟んで指定する。
index.php url=receivers/indexなお、
/usr/local/php-5.2.6/bin/php-cgiは起動するPHPの本体プログラムの指定である。これについては、さくらインターネットでメール受信をリアルタイムに処理する を参照のこと。
-q は、httpヘッダーを出力しないようにする設定である。
次に、receivers_contoroller.php に記述された ReceiversContorollerを起動してみよう。
<?php
class ReceiversController extends AppController{
var $uses = null;
function index(){
$fp=fopen("php://stdin",'r');
$content = null;
while( !feof( $fp ) ){
$content .= fgets( $fp , 4096 );
}
fclose( $fp );
$this->render('void','void');
}
}
?>
これで、$contentにメールの中身が入ることになる。あとは自由に料理しよう。ここで、メールの中身とは、いわゆる本文のことではなく、メールヘッダーも含めたメール全体のことを示すことに留意すること。
Pear::MIMEでも解析できるが、簡単なものであれば、preg_matchで十分だろう。
また、いずれPearを使用しない方式でのメールデコードを紹介したい。
なお
$this->render('void','void');
としたのは、メール起動の場合、画面出力は意味がないからである。しかるべき場所に、layoutのvoid.ctp、viewのvoid.ctpを何も記述しない空ファイルとして配置しておこう。(CakePHP1.1の場合は、void.thtmlとなる。)
*1 : perlの話ですが、参考になるはずです。
■Qdmailと組み合わせて空メール処理をCakePHPのコントローラーで処理する
recevers_controller.phpを以下のようにする。*2
<?php
class ReceiversController extends AppController{
var $uses = null;
var $components = array('Qdmail');
function index(){
$fp=fopen("php://stdin",'r');
$content=null;
while( !feof($fp) ){
$content .= fgets($fp,4096);
}
fclose($fp);
$header = preg_split( '/\r?\n\r?\n/is' , $content );
if( 0 !== preg_match('/from:\s*<?([^<>@ ]+@[^<>@ ]+)>?[^<>@\r\n\s,]*\r?\n/is',$header[0],$matches)){
//---------------------------
// 本来はその他にも、携帯端末からの
// メールであることを確かめる
// ルーチンを置いたほうがよい。
// いたずら、迷惑メールの発信元に
// ならないようにするためである。
// mail address varidationも必要だろう
//--------------------------
$to = str_replace( array("\r","\n") , '' , $matches[1] );
$this->Qdmail->to($to);
$this->Qdmail->from('from@example.com');
$this->Qdmail->subject('空メールのテストです。');
$this->Qdmail->cakeText( $to , 'index' , 'default' );
$this->Qdmail->send();
}
$this->render('void','void');
}
}
?>
view,layoutの置き場は、QdmailをCakePHPで使うをご参照下さい。以下のQRコードを読み取って、空メールを送ってみて下さい。
上記のコードでもって実現した*3返信メールをお送りします。
(あえてメールアドレスは書いていません。)
貴方のメールアドレスは保存しないのでご安心ください。信用できない方はメールを送らないでください。
- TB-URL http://www.cpa-lab.com/tech/0139/tb/
-
▼
CakePHPのシェルコマンドをさくらインターネットで使う
CPA-LABテクニカル メール受信をきっかけにCakePHPコントローラーを起動する では、その名の通り、コントローラーをメール受信で起動させた。ここでは、CakePHP1.2で導入されたシェル機能(shell)を利用し、CakePHPのシェルをメール受信をきっかけに起動させてみ...