忍者ブログ

カウンター

プロモーション

カレンダー

03 2024/04 05
S M T W T F S
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

AntinomyMy の実験室

   私のWEBアプリ実験室です!

ブログ内検索

楽天でお買い物

twitter

最新トラックバック

最新コメント

忍者アナライズ

ウェザーニュース

バーコード

本を買う

アクセス解析

Google+

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


  • 2024/04/27 18:19

mb_internal_encoding 関数(mbstring.internal_encoding 設定) とstrlen 関数,mb_strlen 関数の不思議な関係

PHP の調べ物

さてさて、毎回Web上にある設定の変更できないレンタルのサーバの環境とローカル環境の違いを把握して・・・どうのこうの・・・
みたいなややこしいことをしてきた結果、当たり前であろう事により一段と近づく結論が浮かんだ。

それは設定の修正が効かない側のサーバ(レンタルサーバ)の環境に極力ローカルの開発環境も整えること。

という結論が出た、とても当たり前と言えば当たり前だ・・・。

文字化け以外にも、文字カウントを失敗するないていうことが、
デフォルトの 内部文字エンコーディング ->mbstring.internal_encoding (php.ini内)

関数で指定した内部文字エンコーディング ->mb_internal_encoding 関数
によって起こる事が判明した。

まず、この結論にたどり着くまでに気がついた事柄を述べる。
<まずテストする為のソースコード>
<?php
$string = '内部文字エンコーディング環境のTESTです!'.mt_rand(0,9).mt_rand(0,9).mt_rand(0,9);

// 文字の長さの検出の色々な仕方
var_dump(strlen($string));     //内部文字エンコーディングが違う環境に依存しない(バイト数)
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に確実に左右される
var_dump(mb_strlen($string ,'UTF-8'));  
//内部文字エンコーディングが違う環境に依存しにくい
?>

これを参考に以下、1. 2. 3. を見てもらえば判ると思う。

1.文字数をカウントする計算式に狂いが生じる。
内部エンコーディング:ISO-8859-1

内部エンコーディング:UTF-8
では部分的に違う出力結果になる。

内部エンコーディング:UTF-8 の場合、上から答えが int 61、 int 25、 int 25 となる。
内部エンコーディング:ISO-8859-1の場合、上から答えが int 61、 int 61、 int 25 となる。

2.カウントしたい文字列が1文字が1バイト以上の文字である場合には、
 文字数カウントに使えない関数がある。

つまりこの例のスクリプトの場合は、
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に依存しない
がバイト数でしか文字列がカウントできないです。

そしてまた、文字としてカウントできるとしても、正しい文字エンコーディングを指定していない場合、
存在しない文字を他の文字に読み違えたりして間違って文字を認識したりした結果、
文字数が変わってしまうのが、
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に確実に左右される
です。

また、内部エンコーディングを指定して文字数をカウントさせているので、一番文字数のカウント間違えが
少ないと思われる、
var_dump(mb_strlen($string ,'UTF-8'));  //内部文字エンコーディングが違う環境に依存しにくい
ですが、
依存しにくいと書いているのは、保存形式によって保存された文字が
本当に指定したコードで文字を間違えて文字化けなどをせずに解釈できるのかどうかの
テストは今回行っていないからです。

3.サーバの環境によってデフォルトの設定があるので、
 文字数カウントの関数の引数が任意であっても指定した方が環境が変わっても、
 スクリプトが同様に動く可能性が高くなる。

さて、ではサーバ側が例えばレンタルサーバであって、
php.ini 内のmbstring.internal_encoding の値がない(no value)場合なども考えられる。
その場合には、私のテスト環境のWindows とレンタルLinuxサーバ ではどちらもISO-8859-1 になってしまった。

phpinfo 関数で調べると、ISO-8859-1 になっている箇所が
iconv.input_encoding, iconv.internal_encoding, iconv.output_encoding
だけであったが、この設定以外にISO-8859-1 が使われている場所が
内部的にあるのかどうか?までは調べていない。

1つの環境内でphp.ini 内でデフォルトの内部エンコーディングが変化することなどは
開発環境では望ましくないが、他のサーバの設定も把握してスクリプトを書くのならば、
やはり環境を一致させた状態、つまりphp.ini を一緒にして完全に作るか、
それかスクリプト内部で

// 内部文字エンコーディングをUTF-8に設定
mb_internal_encoding('UTF-8');

とかするしかないと思う。

しかし確実なのは両方の対策をとっているのが望ましい気がした。



最後にphp はHTML やXHTML に埋め込み使うのであるが、
ヘッダーを出力できるという利点がある。

ヘッダーを解釈するのはWebサーバ上のApache などがMIME を解決して
クライアントのブラウザの挙動に繋げているのは前回説明したが、
それ以外にもブラウザの挙動に直接つながっているのだと分かる指定の仕方があった。

//ブラウザがこのスクリプトの出力の文字コードを割り出す為のヘッダー
header('Content-type: text/html; charset=UTF-8');

これを組み込んでおくことと、組み込まずにおくことでは
文字が化けるか化けないかが変わるのである。

ブラウザで確認したのは、IEであるが、
自動でUTF-8 になっているか、それとも今回のスクリプトソースをUTF-8で保存し
header('Content-type: text/html; charset=UTF-8');
をつけず、表示しようとすれば、環境にもよる(php.iniの設定に依存すると思う)が
シフトJISとなってしまって文字化けしてしまうことがある。

しかし不思議なことに、ブラウザのソースを見ても
<html>
<head>
ココ
</head>
などという場所に文字コードの指定や書いた形跡は無い。
それなのに文字コードは間違えがない設定になって表示されるのである。

また、文字コードを間違えない状況になった後でも、
ブラウザの自動選択を指定するとなぜか文字化けを起こすと思う。
これはブラウザが送ったヘッダーで表示の仕方が指定されていたが、
ブラウザの持つ文字の自動認識の方法を使って文字列を再検出した為だろうと
予測される。

これはブラウザの更新(F5キー)でヘッダー関数ででUTF-8 を指定しているのであれば
再び元に戻るのでとても面白い仕組み的な側面が見えると思う。

最後に神経質に直したスクリプトの例を載せておこうと思う。

<?php
//ブラウザがこのスクリプトの出力の文字コードを割り出す為のヘッダー
header('Content-type: text/html; charset=UTF-8');

// 内部文字エンコーディングをUTF-8に設定
mb_internal_encoding('UTF-8');


$string
= '内部文字エンコーディング環境のTESTです!'.mt_rand(0,9).mt_rand(0,9).mt_rand(0,9);

// 文字の長さの検出の色々な仕方
var_dump(strlen($string));     //内部文字エンコーディングが違う環境に依存しない
var_dump(mb_strlen($string));    //内部文字エンコーディングが違う環境に確実に左右される
var_dump(mb_strlen($string ,'UTF-8'));  
//内部文字エンコーディングが違う環境に依存しにくい
?>



こんなこんな感じだろうと思う。


そして余談だが、UTF-8 でソースを保管する場合に、
もしソース中で文字を自身が指定したフォントを用いた画像で出力する為の、
imagefttext imagettftext 関数で画像を使う場合、
保存形式をUTF-8(BOMあり)ではなくUTF-8(BOMなし)にして保存しなければ、
画像データの先頭まで、ファイル形式の構造情報が3バイト付加されてしまうので、
画像データとして用いることができなくなるので注意が必要です。
UTF-16だと2バイトのBOMが付き、それは意味があるそうですが、
UTF-8でのBOMは実際には無意味なものらしいです。
詳しくは、BOM をどうぞ。

また画像を出力するスクリプトに
//ブラウザがこのスクリプトの出力の文字コードを割り出す為のヘッダー
header('Content-type: text/html; charset=UTF-8');
という内容は必要ないです。


ちょっと独り言:
当たり前って難しい・・・当たり前になるって難しい・・・当たり前に成る頃には忘れていたり、大したことがないこと・・・でも大切・・・

以上長いまとめのメモでした。

拍手[0回]

PR


  • 2011/04/15 07:32

コメント一覧

  • お名前
  • Email

  • コメント

  • Vodafone絵文字 i-mode絵文字 Ezweb絵文字
  • パスワード
[PR]