日本PHPユーザ会 掲示板

日本PHPユーザ会運営の掲示板です。※ただいまテスト運用中です。

日本PHPユーザ会 掲示板 » PHP事はじめ » mbstring関数について教えてください。

mbstring関数について教えてください。

ページ: 1

投稿者 投稿
会員
登録者: 12 2007
返信数: 9
はじめまして,よろしくお願いします。PHP初心者でCD付きの解説書で勉強中です。

MySQLからデータを取得して,ブラウザ上に表示するコードを実行すると,漢字の部分が ” ??? ”となってうまくいきません。(OSはXP SP2,ブラウザはInternet Explorer)
文字コードの変換がうまくいっていないように思います。CDに付属しているコードでは次のような変換を行っています。これについて3つ質問があります。

$enc_disp = "EUC-JP"//引数$toに代入;
$enc_db = "EUC-JP"//引数$fromに代入;
// データの文字コードを変換する関数
function cnv_enc($string, $to, $from) {
// 文字コードを変換する
$det_enc = mb_detect_encoding($string, $from . ", " . $to);
if ($det_enc and $det_enc != $to) {
return mb_convert_encoding($string, $to, $det_enc);
}
else {
return $string;
}
}

質問1 mbstring関数は拡張モジュールでインストールが必要なのだそうですが,どのようなものが必要で,どのようにすればよいのでしょうか?

質問2 mb_detect_encodingについて解説してください。2番目と3番目の引数に ”EUC-JP”を指定していますが,同じものを指定してどのような意味があるのでしょうか?第2,第3引数は何のリストなのでしょうか?何か判断でもしているのでしょうか?

質問3 前問の関連ですが,if ($det_enc… とありますが,$det_encは文字列型で,”EUC-JP”などが格納されています。bool型ではないのですから,IF文の条件式としては不適切だと思います。どうしてこのような条件式が成り立つのでしょうか?
よろしくお願いします。
会員
登録者: 12 2007
返信数: 4
こんにちは! 私のわかる範囲でお答えします。
【質問1への回答】
 Windows版PHPを使用しているようですので、PHPインストール時に拡張モジュールも込みでインストールされています。
【質問2への回答】
 まず、「2番目と3番目の引数」とありますが、3番目の引数はありません。"$from, $to"と書けばわかり易いですね。
この例題は、恐らく$enc_db、$enc_dispを読者の環境に合わせて変更して下さいという主旨だと思います。
$enc_db="EUC-JP"; $enc_disp="SJIS"; のようにすればいいですね。
 ご質問の2番目の引数は、下記マニュアルを見て下さい。
http://jp.php.net/manual/ja/function.mb-detect-encoding.php
2番目の引数を省略しても構わないです。その場合、detect_order が使用されます。
print_r(mb_detect_order()); でデフォルトのdetect_order が何かわかります。
【質問3への回答】
 mb_detect_encodingは1番目の引数$stringのencodingが2番目の引数$encoding_list(省略された場合はdetect_order)に無い場合はfalseを返します。if ($det_enc and ... は「falseでなくかつ」を意味しています。
if ($det_enc !== false and ... にするとわかり易いですね!
 また、この判定を入れないと return mb_convert_encoding($string, $to, $det_enc); の$det_encがfalse
になる場合があり、Warningが出て正しくエンコーディングされません。

以上、PHPマイスター目指して頑張って下さい。
会員
登録者: 12 2007
返信数: 9
NewCrayonさん,丁寧なご回答ありがとうございます。助かりました。

"$from, $to" で一つの引数だとは,思いもよらない落とし穴に落ちていました。
「2番目の引数を省略しても構わないです。」なるほど,はっきりとそう言っていただいて納得がいきました。データベースのエンコードがEUC-JPで表示もEUC-JPというのに納得がいってなかったのです。文字エンコーディングの リストを指定するのに,同じもの2つというのはいかにも変ですよね。
この2番目の引数は,これらのエンコーディングのリストの中から,当てはまるものを文字列として出力しなさいということなんですね。?(たぶん?) でも,そうだとしたら,やはり第2引数を省略して,より多くのエンコーディングの中から適合するエンコーディングを出力させたほうがいいですよね。

でも,ここで,第2引数を指定する理由は,たぶん,次のIF文があるからなのでしょうね。
対象となる文字列が,$enc_dbと,$enc_dispの2つのエンコーディングのどちらかで,$toのエンコーディングと一致しない場合にのみ,コンバートして,それ以外のときは,コンバートせずにそのまま文字列を返しなさい。ということなのですね。

なるほど,納得がいきました。(これでいいのですよね?)
ただ,今回の障害で思ったのですが,ASCIIや JIS, UTF-8, SJISなどの他のエンコーディングのときは,ブラウザに,正常に表示されるから,コンバートしなくてもいいということなのでしょうか?Windows以外の環境や様々なブラウザのどれにでも表示できるようにするには,どのエンコーディングにコンバートすれば良いのでしょうか?
そもそも,自分のデータベース(MySQL)内のデータのエンコードが何で記述されているのかが不明です。どこを調べれば良いのでしょうか? mb_detect_encodingで調べたらASCIIと出てきたのですが,Select文でデータを表示させたとき,同じ漢字の部分でも,入力の方法によっても,正常に表示されたり,????と表示されたりと,もしかしてコードが違うのでは?とも考えています。

まだまだ,分からないところだらけですが,この件に関してはおかげさまで一応の納得がいきました。これからも,よろしくお願いいたします。
会員
登録者: 10 2007
返信数: 71
>mb_detect_encodingで調べたらASCIIと出てきた

試しにこちら↓のSQL文を、MySQL接続直後に実行してみたらどうなりますか?

SET NAMES ujis


これはMySQLに対して「EUC-JPでやりとりしましょうよ」と指示している「らしいです」。
会員
登録者: 12 2007
返信数: 9
こんばんは,返信が遅れて申し訳ありません。
やってみました。
mysql_query("SET NAMES ujis")と記述して実行したら,おっしゃるとおり,データベースから取り出した文字列は"EUC-JP"であると出力されました。

私の環境では,結果的に,"SET NAMES utf8"と記述するとうまくいくことが分かりました。
私は,まだ,レンタルサーバーを決めていないので,これから記述したコードをどんな種類のエンコードにすればよいのか?ますます悩ましい問題のようです。

MySQLでは,データベース,テーブル,またフィールドごとにキャラクターセットを指定できるそうですが,別のキャラクターセットが格納されると文字が壊れてしまうそうですね。厳格に文字コードを指定して,いちいち文字列をコンバートしてデータベースから出し入れするのが正しいやり方なのでしょうか?キャラクターセットとかエンコード(文字コード)の取り扱い方法について???分からないことだらけです。どのようにすれば多くのブラウザに正しく表示されるプログラムが書けるのでしょうか?
そんなことは無理なのでしょうか?
会員
登録者: 12 2007
返信数: 4
 お早うございます。苦戦しているようですね!でも、悩むことはいいことです。php習得の早道です。
【質問1】ブラウザ上に表示するコードを実行すると,漢字の部分が ” ??? ”になる。何故か?
【質問2】厳格に文字コードを指定して,いちいち文字列をコンバートしてデータベースから出し入れするのが正しいやり方なのでしょうか?
【質問3】キャラクターセットとかエンコード(文字コード)の取り扱い方法について???分からないことだらけです。
【質問4】どのようにすれば多くのブラウザに正しく表示されるプログラムが書けるのでしょうか?

 まず、私が知る範囲でphpが文字列を出力するメカニズムを文字コードに限定して説明します。
【前提】下記(2)(3)(4)について、自環境のphp.iniがどうなっているか確認要(phpinfo();でもOK)。
(1) 下記スクリプトはWindowsでSJISで書かれていることを前提
(2) 出力の変換を有効にするために output_handler = mb_output_handler を仮定
(3) 内部エンコーディング mbstring.internal_encoding = EUC-JP と仮定
(4) 出力エンコーディング mbstring.http_output = SJIS と仮定
【説明】
 1. スクリプト内で「echo "クレヨンしんちゃん";」(SJIS)を実行
 2. phpは、内部エンコーディングがEUC-JP(3)なので"クレヨンしんちゃん"がEUC-JPであると思い、
  SJIS(出力エンコーディング(4))に変換する。
 3. これは、「mb_convert_encoding("クレヨンしんちゃん", 'SJIS', 'EUC-JP');」に等しく
  SJISの"クレヨンしんちゃん"を強引にEUC-JPに変換するので、正しく変換されない。
 4. 3.を具体的に見て見る(文字列だとわかりにくいので16進表示で)と、下記となる。
   [text] => クレヨンしんちゃん
   [text(encoding)] => SJIS
   [text(hex)] => 834e838c8388839382b582f182bf82e182f1 → SJIS "クレヨンしんちゃん"の16進表示
   [mbstring.internal_encoding] => EUC-JP
   [mbstring.http_output] => SJIS
   [EUC-JP->SJIS(hex)] => 3f4e3f3f3f3f3f3f3f3f3f3f3f → SJISを無理やりEUC-JPに変換した結果
   [EUC-JP->SJIS(text)] => ?N??????????? → 上記を文字列として表示した結果

 5. 上記のように文字化け ?N??????????? して表示される(変換できない文字は?になる)。
 6. "クレヨンしんちゃん"が EUC-JP なら 834e838c8388839382b582f182bf82e182f1 と変換され、
  出力エンコーディングのSJIS通り 834e838c8388839382b582f182bf82e182f1 がブラウザに送られ、
  ちゃんと表示される。

【結論】
 1. 内部エンコーディングに合わせてスクリプトを記述する。
 2. 外部データ(データベース等)は内部エンコーディングと同じにするか、スクリプトで変換する。
 3. レンタルサーバだとphp.iniで内部エンコーディングを勝手に設定できないので、
  スクリプト内で、(例)「mb_internal_encoding('SJIS');」により内部エンコーディングを変更する。
  (これは、スクリプト内だけで有効であり、サーバの設定に影響はない)
 4. 出力エンコーディングの設定はあまり考えなくても大丈夫です。サーバから正しいコードで文字列が
  送られれば、SJSIでもEUC-JPでもUTF-8でも大抵のブラウザは表示することができます。
  ちなみに、サーバは出力エンコーディングで設定されたエンコーディングをhttpレスポンスヘッダの
  charsetにセットしてクライアント(ブラウザ)に送ります。ブラウザはこのcharsetにより表示を
  切替えます(と思う)。
  
 どうでしょう?以上で全質問にお答えしたと思っておりますが・・・。

p.s
 phpでのキャラクタセットやマルチバイトの取扱いについては下記一読をお薦めします。
http://www.php.net/manual/ja/ref.mbstring.php
会員
登録者: 12 2007
返信数: 9
NewCrayonさん,ありがとうございます。

私の環境ですと
echo mb_internal_encoding();と記述すると"UTF-8"とでてくるので,内部エンコーディングというのは,"UTF-8"なんだと思います。
お話によると,プログラム(コード)はすべて,"UTF-8"で保存してやればよいということなんですね。そして,データベース等から取り出された文字列等のデータ(外部エンコーディング)は,すべてUTF-8に変換してから操作すれば良いということなんですね。
確かに,おっしゃるとおりにすると,表示はすべてうまくいきます。"EUC-JP"などを使っているとうまくいかなかった理由が分かったような気がします。

となると,PHPの内部エンコーディングについては,ある程度クリアになったような気がします。ただ,残るのは,MySQLの内部での取り扱いが不明です。
私は何冊かのPHPおよびMySQLの解説書を所有しています。これらに付属しているCDに入っていたサンプルコードを取り出していろいろと試してみています。CDに付属しているデータベースのデータも様々なキャラクターセットで記述されているデータが混在しているようです。
MySQL>のコマンドラインからSELECT句でレコードを表示させてみると,きちんと漢字で表示されるものと,やはり'????'などのように表示されるものがあります。
私の環境では, mysql> コマンドラインに show variables like "char%";と入力すると

| Variable_name | Value
---------------------------------------------
| character_set_client | sjis
| character_set_connection | sjis
| character_set_database | sjis
| character_set_filesystem | binary
| character_set_results | sjis
| character_set_server | sjis
| character_set_system | utf8
| character_sets_dir | C:\Program Files\MySQL\MySQL Server 5.0\shar\charsets\ |

とキャラクターセットが出てきます。この中のどれが,データベースからの入出力を決定付けているのでしょうか?(これだと多分,'sjis'か'utf8'のどちらかのようですが…)
また,OSやブラウザの違いによって,入力されるときに使われるキャラクタセットもそれぞれ異なるわけなんですよね?(この辺がいまいち分かりません。) もしそうだとしたら,データベースへのデータの挿入やデータベースからのデータの読み込み(表示)において,常に,このキャラクターセットなるものを意識しなければならないと言うことなんですね???(フー,この辺で悶々としていて,なかなかコードを記述するまでに至りません。)
これからも,よろしくお願いいたします。
会員
登録者: 10 2007
返信数: 71
> MySQL>のコマンドラインから

試しにMySQLクライアントでSET NAMES ***;を(***はujisとかutf8とかsjisとか)実行してみると、ちゃんと対応してくれているようです。
1) Windowsのtelnetでサーバにログインし、サーバのMySQLクライアントを使った場合、WindowsのtelnetはシフトJISをそのまま表示するので、SET NAMES sjis;をやっておくと思惑通りに表示されました。SET NAMES ujis;やSET NAMES utf8;では文字化けしました。
2) PHPで自作したSQLクライアントを使った場合、PHPの内部エンコーディングがEUC-JPなので、SET NAMES ujis;をやっておくと思惑通りに表示されました。SET NAMES sjis;やSET NAMES utf8;では文字化けしました。
どちらも同じテーブルで同じエンコーディングujisです。

というわけで私的には、PHPの内部エンコーディングに合わせてSET NAMES ~;をしてあげればPHPで受け取る際に変換する必要もないし、さらにテーブルも同じエンコーディングで作ってやればパフォーマンスも向上するらしいので、そういうようにしています。
« 最終編集者 kona@m 日時 2008 年 1 月 10 日(木) 13:15. »

ページ: 1

日本PHPユーザ会 掲示板 » PHP事はじめ » mbstring関数について教えてください。

日本PHPユーザ会 掲示板 は UseBB 1 フォーラムソフトウェア を使用しています