人気ブログランキング |
タグ:文字コード ( 6 ) タグの人気記事
(PHP)Smartyを使っているがShift-JISで出力したい
Shift-JISで作られているCGIのサイトをPHP(テンプレートエンジンとしてSmarty使用)に移行してるんですが、やはり移行後もわけあってShift-JISでないとダメなようです。
で、SmartyのテンプレートファイルをShift-JISで作るとエラーが。。。
どうやら「必須」という文字がSmartyのメタ文字「{」となっちゃってるようです。
やはり、Shift-JISのファイルは作っちゃダメだなということで、テンプレートファイル、PHPファイル共にUTF-8で書き、Smartyのアウトプットフィルタを使って出力時にShift-JISに変換することとしました。

以下のような感じです。
//Smarty読み込み(ドキュメントルート外のSmartyクラスにアクセス)
require_once('Smarty.class.php');
//Smartyオブジェクト作成
$smarty = new Smarty();
$smarty->template_dir = "/smarty/templates";
$smarty->compile_dir = "/smarty/templates_c";
$smarty->cache_dir ="/smarty/cache";
 
//アウトプットフィルタを使用し文字コード変換
$smarty->registerFilter("output","filterSjis");
ini_set("default_charset", "Shift_JIS");
 
//テンプレート表示
$smarty->display("hoge.tpl");
 
//smartyアウトプットフィルタ
function filterSjis($buff, &$smarty){
return mb_convert_encoding($buff,"SJIS","UTF-8");
}


参考サイトでは $smarty->register_outputfilter を使っているですが、Smarty3でこのメソッド使うと、"Notice: function call 'register_outputfilter' is unknown or deprecated." みたいなエラーになります。
Smarty3系からは $smarty->registerFilter(タイプ,コールバック関数); としないといけないようです。


参考:
Smartyのアウトプットフィルタを使ってみる kawama.jp
エピゴーネンで | register_prefilterの挙動を変えるとかなしにしようぜSmartyさん
SJISページでのSmartyの使い方 | ガイドミー管理者日記
by Jehoshaphat | 2012-11-29 23:18 | PHP開発
(PHP)AjaxUploadで画像が表示されない原因は、、、BOMだった..
(PHP,HTML)AjaxUpload 画像選択時にアップロードしサムネイルを表示を実装していたんですが、なぜかサムネイルが表示されない現象が発生しました。


Ajaxで返すPHP側を絶対パスで返すようにIE8で画像のプロパティを表示すると、 http://hoge.local/http://hoge.local/img/up.jpg といったように正しくパスが解釈されません。
で、Firefoxで確認すると、画像パスが http://hoge.local/%EF%BB%BF%EF%BB%BFhttp://hoge.local/img/up.jpg となっています。

ん? EF BB BF ってどっかでみたことあるぞ と思ったら、(PHP)UTF8でBOMがあるとブラウザ表示時レイアウトがおかしくなるで以前ハマッた現象でした。

そうです。reuireしている別PHPファイル2個が UTF-8 の BOM が付いていたのです。

BOM無しで保存しなおすことで、正しく表示されるようになりましたが、気づくのにかなりの時間がかかってしまいましたorz。

PHPでUTF-8を使う場合は、必ずBOM無しって覚えておかないといけませんね。。。
by Jehoshaphat | 2012-11-06 19:43 | PHP開発
(PHP)レンタルサーバで文字コード固定されている時の文字化け回避法
現在作成中のPHPスクリプトを複数のレンタルサーバに配備する予定です。
テンプレートエンジンに、Smarty を使い、テンプレートファイル(HTML),PHPファイル共に文字コードは UTF-8 を使用予定で、その文字コードで記述しています。

で、とあるレンタルサーバはPHP,apache共に文字コードが EUC-JP となっていました。

テンプレートファイル(HTML)はメタタグで下記のように文字コードは指定しています。

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

なのに、表示すると文字化けしました。

メタタグで、UTF-8に設定したHTMLファイルだけだとちゃんとUTF-8で表示されます。

文字化けするレンタルサーバからのHTTPのヘッダ情報を FireBug を使ってみてみるとこうなっていました。

<pre>
Content-Type: text/html; charset=EUC-JP
</pre>

どうやら、HTTPヘッダのContent-Typeが有効になっているようです。
@IT:Webブラウザが文字コードを判定する基準は?を見ると、ブラウザは HTTPにおけるContent-Typeヘッダのcharsetパラメータ → HTML文書内のMETA宣言Content-Typeヘッダのcharsetパラメータ → HTML文書内の各要素のcharset属性 という順序で、文字コードを決定するようです。

レンタルサーバによってはHTTPヘッダに文字コードを埋め込んでるところもあるようなので、そういう場合は、こちらでHTTPヘッダを変えてるやる必要があるようです。

ということで、PHP 側で、下記を追加することで解決しました。

header("Content-type: text/html; charset=UTF-8");

レンタルサーバのPHPの既定のエンコーディングが、自身の意図しているものと違う場合は、上記の header と共に下記も指定しておくといいかもしれませんね。

mb_language("Japanese");
mb_internal_encoding("UTF-8");

ということで、util.phpみたいなのを用意し、メインのPHP側に読み込ますような感じとしました。
○util.php

<?php
 
/**
* 初期化処理。
* 文字コードの設定。
* 各ページの最初に呼ぶこと。
*/

function Init(){
header("Content-type: text/html; charset=UTF-8");
 
mb_language("Japanese");
mb_internal_encoding("UTF-8");
}
 
/**
* ログインしているかのチェックを行う。
* セッションも開始する。
* ログインしていなければログインページに飛ばす。
*/

function LoginCheck(){

session_start();

//認証できてなければログインページへ
if ( ! isset( $_SESSION["username"] ) ){
header("Location:index.php");
exit;
}
}
?>


○index.php

<?php
require_once 'util.php';
//初期処理
Init();
 
..処理、Smarty呼び出し、描画
 
?>


後、ヘッダ系の関数使うときは、その間数より前に出力をしていると下記のようなエラーとなります。

Warning: Cannot modify header information - headers already sent by (output started at C:\...\util.php:10)
in C:\...\util.php:10

特に、今回の util.php のような共通関数の入ったPHPを作る時は、<?php >の範囲外に空白や改行や文字を入れないようにしなければならりません。

例えば下記はNGのようです。

<?php
// 共通関数
?>
 
[EOF]

下記のようにすべきです。

<?php
// 共通関数
?>[EOF]


久しぶりのPHPとだけあって、結構ハマってます。。。。

参考:
PHPでの質問です
by jehoshaphat | 2011-07-01 02:05 | PHP開発
XPでJIS第三、第四水準の漢字を表示したい
XPを使ってるユーザからJIS第三、第四水準の漢字を表示したいという要望がありました。

実はWindowsVista以降は、MS明朝やMSゴシック等のフォントはJIS X 0213:2004(通常JIS2004)に対応し、第三水準、第四水準の漢字も表示できるようサポートされています。
ただし、JIS2004では例示字体が変わったので、第一、第二水準の漢字でも一部の漢字の字体が変わってしまっています。

このあたりの話はVistaが発売された時に話題になりましたね。
で、マイクロソフトはXPでもVista以降で使われてるJIS2004対応フォントを提供してるので、それを入れると第三、第四水準の文字が使えます。
ただ、JIS2004で変更された例示字体になってしまいますけどね。

フォントのダウンロードはMicrosoft Windows Vista:Windows XP および Windows Server 2003 向けJIS2004 対応 MS ゴシック & MS 明朝フォントパッケージについてからできます。
また、もJIS2004に対応してるようです。

Vistaや7に入っているJIS2004対応フォントにはjp90タグという仕掛けがあり、JIS X 0208-1990の字体で文字を表現することができるようです。つまり、XPの時と同じ字体となるわけです。(ただし、厳密に同じというわけではないようなので注意が必要です。また、jp90タグを使うにはタグを指定できるアプリケーションじゃないとダメなようです。)

また、JIS2004対応になると、第三、第四水準の文字が4バイトになるのでアプリケーションによっては正しく動かなかったりするので注意が必要みたいです。

漢字ってのはホントややこしいですね。

参考:
ITpro:VistaをXPの字体に戻すというjp90タグの罠
ITpro:Vistaで化ける字,化けない字
ITpro:第1回:進まないJIS2004への移行,その原因は?
サロゲートペア入門:CodeZine

補足:
JIS2004で変更された表記(例示字体)は経済産業省のサイトにもPDFで配布されてました。
JIS2004対応フォントいれるときに、この資料をユーザに渡すといいかもしれません。
JIS漢字コード表の改正について-報道発表-経済産業省
by jehoshaphat | 2011-01-17 22:07 | 豆知識
(PHP)HTML側がShift-JISだと SET NAMES sjis でのMySQLクエリはおかしくなる
PHPで Shift-JIS使うなってことは前から聞いてたんで、今まで使ってなかったんですが、今回は携帯向けってことで仕方なくHTML側でシフトJIS使うことになりました。

環境としては下記のような感じです。

MySQL<--->PHP<---->HTML(Smarty)
UTF-8 UTF-8 Shift-JIS

MySQLには MySQL Administrator を使ってテストデータを入れています。

問題に気づいたのは下記のような HTML 側のテキストエリアに入れた文字をDBで検索する部分を作っていたときでした。
●HTML側(文字コードは シフトJIS)
<form id="order" action="find.php" method="GET">
<input type="text" name="searchname" size="10" maxlength="100" >
<input type="submit" name="search" value="検索"><br>
</form>

●PHP側(find.php)
$findStr = trim(urldecode($_GET["searchname"]));
echo "入力データ:" . $findStr . "<br>";
//DB接続省略
mysql_query('SET NAMES sjis');
//クエリ生成
$query = sprintf("SELECT * FROM tablename WHERE name LIKE '%s' ",
mysql_real_escape_string($findStr ));
//クエリ実行
$result = mysql_query($query);
 
while ($row = mysql_fetch_assoc($result)) {
echo $row["store_name"] . "<br>";
}

で、DBに MySQL Administrator で name列に文字化けやすい代表文字である "表" という文字をいれ、上記のHTMLのテキストボックスに "表" といれ検索しました。
本来ならヒットしないといけないのに、ヒットしません。


いろいろググって見るとマジッククォートっていうものがあるようです。
詳細は後ほど書くとして、これはOFFになってるので、関係なさそうです。


おそらく Shift-JIS が悪いんだろうってことは予想ついてるんですが、それをはっきりさせるために、HTML側を EUC-JP にし、SET NAMES も EUC-JP(ujis) にしてやってみました。
すると、ちゃんとヒットします。

やはり Shift-JIS が関係してますね。


条件追加した後のクエリとかも除いてみましたが、特に問題なさげです。
さらにググるとDBへのデータ挿入で文字化けっていう事例が多いので、ためしに INSERT するクエリを作ってみました。
$findStr = trim(urldecode($_GET["searchname"]));
echo "入力データ:" . $findStr . "<br>";
//DB接続省略
mysql_query('SET NAMES sjis');
 
$query = sprintf("insert into tablename ( name ) values ( '%s')",
mysql_real_escape_string( $findStr ));
$result = mysql_query($query);
echo mysql_error();


これで、HTMLに"表"って入れてPHPからDBに登録してみました。
すると、下記のようなエラーが。。。

You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for
the right syntax to use near ''表\' , 1 )' at line


やっぱり、MySQL内でのクエリ実行時には勝手にエスケープされてます。

SET NAMES と mysql_set_charset は内部的に違う処理をしてるようです。
そこら辺は、symfony × MySQL × Shift_JIS: 0×5c関連:で詳しく説明されてました。
簡単に言うと、mysql_set_charset()を使った場合はmysqlコネクションオブジェクトに文字コードを設定するため、Shift-JISの2バイト目の0x5cをエスケープしたりしないようです。

MSN相談箱:MySQL5.1の文字化けにも SET NAMES ではなく mysql_set_charset() を使うように提案されています。

ただ、今回使うレンタルサーバはPHPのバージョンが5.1のため mysql_set_charset() が使用できません。


ということで、どっかで書いていたようにGETやPOSTの値を別の文字コードに変換して、SET NAMES のその文字コードでやってやるという方向に落ち着きました。
こんな感じです。
$findStr = mb_convert_encoding(trim(urldecode($_GET["searchname"]) , "EUC-JP", "SJIS"));
//DB接続省略
mysql_query('SET NAMES ujis');
$query = sprintf("insert into tablename ( name ) values ( '%s')",
mysql_real_escape_string( $findStr ));
$result = mysql_query($query);
echo mysql_error();


ただ、上記の場合 Shift-JIS から EUC-JP に変換してますが、これを UTF-8 に変換するときはまた注意が必要のようです。
その場合は、"SJIS" では無く "sjis-win" を使うのがベータみたいですね。
mb_convert_encoding( $string , "UTF-8", "sjis-win");


ここら辺の話は、下記サイトに乗ってました。
-OASIS- - 今日のメモ「mb_convert_encodingの文字化け(PHP)」
地方で活動するweb制作者の日々を綴るblog:PHPで UTF-8←→SJIS の変換を行う場合の注意
( しゃいん☆のブログ ):[php] mb_convert_encoding と UTF-8 の誤変換問題


さて、途中でチラッと出てきたマジッククォートですが、これが有効になってると Shift-JIS の場合さらにややこしくなるようです。
(PHP.ini では magic_quotes_gpc という項目で設定できます。)
レンタルサーバによってはこれが on になっているところが多いんですよね。(まぁセキュリティ対策といえば確かにそうかもしれません)


マジッククォートが有効になっていると GET,POST,Cookie でのユーザからサーバに来る文字の中に、'(シングルクオート)、" (ダブルクオート)、\(バックスラッシュ) 、NULL があれば自動的にバックスラッシュでエスケープするようです。

で、HTML側がShift-JISだと、文字の2バイト目の値が 0x5c だと、これをバックスラッシュとして勘違いし、勝手にエスケープ(バックスラッシュをさらに加える)するようです。(文字コード 0x5c 単体は\と定義されてるため)

たとえばHTMLで "表"(0x 95 5c) と打ってサーバに送信すると、PHPで受けたときには、"表\"(0x 95 5c 5c)となっちゃうわけですね。

対処法としては、Magic Quote GPC機能をoffにする、文字コードにShift-JISを使わない、受信した文字列に「\」を取り除く処理を行う があるようですが、今回の場合だと、前二つは要件、サーバ環境的に無理なので、最後の3つめを行うことに。

下記のように Magic Quote GPC機能が有効かどうかを見て、有効なら余計なバックスラッシュをのけるため stripslashes() を走らせばいいようです。
if(get_magic_quotes_gpc()) {
$inputStr = stripslashes( $inputStr );
}

これはサニタイズと同じように関数化して必ず書くようなクセつけとくといいかもしれませんね。

ここら辺の詳細は、第3回 PHP と Shift-JIS 環境での文字化けについてに詳しく載っています。


しかし、ほんと文字コードがらみは厄介ですわ。。。。
by jehoshaphat | 2009-09-15 18:01 | .Net開発
(VB.Net)String型変数の値(文字列)を16進数でダンプする
デバッグ中に文字列を16進数でダンプする方法です。

エンコーディングを指定し、そのエンコーディングで文字列をByte型配列にします。
(WindowsはAPI等内部的に文字列はUTF-16で処理しているので、エンコーディングはUnicode(UTF-16)にしておきます。)
その後、数値書式指定でX2で2桁16進数に出力します。
下記は引数に渡された文字列をコンソールに出力する例です。

Private Sub DumpString(ByVal str As String)
' エンコーディングはUnicode
Dim sourceEncoding As System.Text.Encoding = System.Text.Encoding.Unicode

'文字列をバイト型配列に変換
Dim bytAry As Byte() = sourceEncoding.GetBytes(str)
For Each value As Byte In bytAry
'16進数2桁でコンソールに出力

Console.Write("{0:X2} ", value)
Next
Console.WriteLine()
End Sub

たとえば "ABCあいうえお" という文字列を上のメソッドでダンプすると下記のようになります。

41 00 42 00 43 00 42 30 44 30 46 30 48 30 4A 30

さて、ここで注意しないといけないのはこの出力結果がリトル・エンディアンになっていることです。
リトルエンディアンとはUTF-16の文字コードでコードの上位バイトと下位バイトが入れ替わったバイト列のことです。
つまり、上記の結果だと通常の符号単位列としてみるにはダンプ結果を1バイトずつ交互に入れ替えます。

A B C あ い う え お
00 41 00 42 00 43 30 42 30 44 30 46 30 48 30 4A

あと、注意しないといけないのはUTF-16は基本的には2バイトで1文字あらわすのですが、2バイトに割り当てられなかった漢字は4バイトで表すということです。

参考:
Wikipedia UTF-16
Unicodeの概略を理解する
ITpro 文字コードの基本
バイト列と数値を変換するには?
by jehoshaphat | 2008-12-29 23:41 | .Net開発