14 October 2008

(Perl + MySQL) 文字列のエンコード UTF-8, Shift-JIS …

MySQLに格納される文字列の標準エンコード形式はUTF-8になっている(MySQL 4.1以降、RHELではutf8がデフォルト)。

Perlでプログラムを作成する時に、UTF-8文字列を扱う方法をリストアップする。

(1)まず、MySQLの文字コードのシステム設定を調べる

mysql> show variables like 'char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | latin1 | | character_set_connection | latin1 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | latin1 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec)

サーバやDBの文字コードはUTF-8である。(なぜかクライアント側設定がlatinになっている…)

クライアント側の文字コードは、使用しているターミナルの文字コードにあわせないと、英数字以外表示できない状態になっている。試しに、ターミナルでの表示状態を見てみる。

mysql> select idx,name from felica_auth_tbl; +-----+-------+ | idx | name | +-----+-------+ | 1 | ??? | ← 日本語の部分が異常 | 2 | Suica | +-----+-------+ 2 rows in set (0.00 sec) mysql> set names utf8; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec) mysql> select idx,name from felica_auth_tbl; +-----+-----------+ | idx | name | +-----+-----------+ | 1 | 八達通 | ← 正常に表示 | 2 | Suica | +-----+-----------+ 2 rows in set (0.00 sec)


(2)PerlスクリプトをSJISで記述する場合

#!/usr/bin/perl use strict; use CGI; use DBI; use Encode; # MySQL接続用変数 my $strSqlDsn = 'DBI:mysql:database=auth_db;host=localhost;port=3306'; my $strSqlUser = 'authuser'; my $strSqlPassword = 'authpassword'; # MySQL接続 my $dbh = DBI->connect($strSqlDsn, $strSqlUser, $strSqlPassword); if(!$dbh) { die('DB connection error'); } # クライアント側文字コードの指定 $dbh->do("set names sjis"); # SQLクエリ文 my $sth = $dbh->prepare("SELECT name FROM felica_auth_tbl WHERE idm_pmm = '01020304'"); # SQLクエリ実行 if(!$sth->execute()) { $dbh->disconnect(); die('DB Query Error'); } # 読み出されたデータ数が1であることをチェック my $nRows = $sth->rows; if($nRows != 1) { $sth->finish(); $dbh->disconnect(); die('DB data count is not 1'); } # 1個目のデータを取り出す my $arrayData = $sth->fetchrow_arrayref; my ($strUserName) = @$arrayData; # MySQL切断 $sth->finish(); $dbh->disconnect(); print "ユーザ名: " . $strUserName;


(3)PerlスクリプトをUTF-8Nで記述する場合

(2)のプログラムの、MySQL文字コード指定部分のみ変更すればよい。以下、プログラムの変更部分の抜粋

~ 略 ~ # MySQL接続 my $dbh = DBI->connect($strSqlDsn, $strSqlUser, $strSqlPassword); if(!$dbh) { die('DB connection error'); } # クライアント側文字コードの指定 $dbh->do("set names utf8"); ~ 略 ~

(4)PerlスクリプトをUTF-8Nで記述するが、MySQLのクライアント文字コードをSJISにする場合

この場合は、Encode::from_to関数で文字コードを変換します。

~ 略 ~ # MySQL接続 my $dbh = DBI->connect($strSqlDsn, $strSqlUser, $strSqlPassword); if(!$dbh) { die('DB connection error'); } # クライアント側文字コードの指定 $dbh->do("set names sjis"); ~ 略 ~ Encode::from_to($strUserName, 'sjis', 'utf8'); print "ユーザ名: " . $strUserName;

(5)PerlスクリプトをUTF-8Nで記述し、use utf8; 宣言を行う場合

外部からやってきた文字列には、たとえそれがUTF-8エンコードされていたとしても、UTF-8フラグを強制的に立ててやる必要がある。
※ 『 use encoding 'utf8'; 』 の場合は、フラグを立てる処理は必要が無い。

#!/usr/bin/perl use strict; use CGI; use DBI; use Encode; use utf8; ~ 略 ~ # クライアント側文字コードの指定 $dbh->do("set names utf8"); ~ 略 ~ print "ユーザ名: " . Encode::decode('utf8', $strUserName);


(6)その他、UTF-8関連で不具合出そうなもの

スクリプト先頭で 『 use encoding 'utf8'; 』 を行った場合でも、MySQLから返される文字列がSJISの場合は、Encode::from_to関数で変換すれば問題ない。 『 use utf8; 』 を指定した場合は、SJISの文字列は正しく扱われない。

参考リンク

Perl 5.8.x Unicode関連
【MySQLウォッチ】第36回 文字化けのメカニズム