SQLite Tips(sjis)

作成:2006.0915
編集:2006.09.15


【SQL】
文字化け
【現象】
shift-jisやeucで
データに標準関数を使用すると
予期せぬ
文字化けを誘発します。
【対応】
マルチバイトに対応したユーザー関数を組み込む
utf8でデータ扱う
SELECT '新',lower('新');
表示結果 2.x, 3.x sjis euc utf8
'新'
lower('新')
SELECT '新品', substr('新品', 1, 1),substr('表', 1, 1);
表示結果 2.x sjis euc utf8
'新品' 新品 新品 新品
substr('新品', 1, 1) - ? ?
substr('表', 1, 1) - ? ?
表示結果  3.x sjis euc utf8
'新品' 新品 新品 新品
substr('新品', 1, 1) - ?
substr('表', 1, 1) \
注) - ?  は表示不能
SELECT like('_\','表'), '表' like '_\' 
表示結果 2.x sjis euc utf8
like('_\','表') 1 0 0
'表' like '_\' 1 0 0
表示結果 3.x sjis euc utf8
like('_\','表') 1 0 0
'表' like '_\' 1 0 0
まとめ
2.x 上の例からもわかるように
文字列関係の標準関数を利用すると
意図しない結果が返ります。

文字列関係の標準関数を使わず、
ユーザー定義関数を使いましょう

ユーザー関数で 標準関数を上書きすることができます。
注意キーワード例:
  like, substr, lower, ........
3.x 文字操作系の標準関数を使う場合は
utf8でデータを扱いましょう
  ユーザー関数で 標準関数を上書きすることができます。

・likeの動作が説明書と違う。
  式 like 文字列式
  like(文字列式, 式,)
※ like(x,y)  : 実行すると説明書と位置が逆<like(y,x)>。 誤実装?   誤植?
 
sqlite/lang_expr.html
にも書いてありますが、

A bug: SQLite only understands upper/lower case for 7-bit Latin characters. 
LIKE operator is case sensitive for 8-bit iso8859 characters or UTF-8 characters.
そういうことで、utf8でデータを操作した方が無難ですね。
確認用コード
php5 <?php
  $charsetlist = array('sjis'=>'shift_jis', 'utf-8'=>'utf-8', 'euc-jp'=>'euc-jp');
  $charset = 'sjis';
 
  switch ($_GET['charset'])
  {
    case 'utf-8':
      $charset = 'utf-8';
      break;
    case 'euc-jp':
      $charset = 'euc-jp';
      break;
    case 'sjis':
      $charset = 'sjis';
      break;
    default:
      $charset = '';
      break;
  }
?>

<html>
<head>
<?php
  echo sprintf('<meta http-equiv="Content-Type" content="text/html; charset=%s;"'
   , $charset ? $charsetlist[$charset] : 'shift_jis');

  echo sprintf('<title>%s</title>'
   , $charset ? $charsetlist[$charset] : 'shift_jis');

?>
</head>
<?php

  if (!$charset) {
    $uri = $_SERVER["SCRIPT_NAME"];
    print '<frameset rows="*,30%,30%">'."\n";
    print sprintf('<frame name="top" src="%s">', $uri.'?charset=sjis')."\n";
    print sprintf('<frame name="bottom" src="%s">', $uri.'?charset=utf-8')."\n";
    print sprintf('<frame name="bottom1" src="%s">', $uri.'?charset=euc-jp')."\n";
    print '</frameset>'."\n";
  }
   else
{  
  print "<body>";
  mb_language('ja');
    $sql = array();
array_push($sql , "SELECT '新',lower('新');");
array_push($sql , "SELECT '新品', substr('新品', 1, 1),substr('表', 1, 1);");

  $dbh = array();
  $user = $pass = '';
  $dbh['3'] = new PDO('sqlite::memory:', $user, $pass);
  $dbh['2'] = new PDO('sqlite2::memory:', $user, $pass);
 
  print "charset[$charset]<br>\n";

  function print_table($items ,$options=array())
  {
     global $charset;
    print  '<table border="1" cellpadding="5" cellspacing="0" bordercolor="#008000">';
    print sprintf('  <tr> <td bgcolor="#F8FFE6">%s %s.x</td>'
             .'<td bgcolor="#F8FFE6">%s</td> </tr>'
, mb_convert_encoding('表示結果', $charset, 'auto')
, $options['ver']
, $charset );
         
     foreach($items as $key => $value)
  {
     if (is_int($key)) { continue; }
     print "<tr>";
     print '<td align="center" bgcolor="#CCFFCC">'.htmlspecialchars($key).'</td>';
     print '<td align="center">'.htmlspecialchars($value).'</td>';
     print "</tr>";
  }
    print  '</table>';
  }

  foreach($dbh as $key => $value)
  {
     if (!$dbh[$key])
{
    $s = sprintf("sqlite[%s]に接続できませんでした。", $key);
    $s = mb_convert_encoding($s, $charset, 'auto');

    print($s."<br>\n");
    continue;
}
print ("<br>\n");

     foreach ($sql as $sqlkey => $sqltext)
  {
     $sqltext = mb_convert_encoding($sqltext, $charset, 'auto');
     print "[sql] ".htmlspecialchars($sqltext)."<br>\n";
     foreach ($dbh[$key]->query($sqltext) as $row)
  {
print_table($row ,array('ver'=>$key));
      //  print_r($row);
  }
  }
     $dbh[$key] = null;
  }
 
}

  print "</body>";
?>
</html>

標準関数上書き編  
substr


pdoの例
mb_language('ja');

$charset = 'euc-jp'; // dbで使う文字コードを指定

function sqlite_ufunc_mb_substr($p1, $p2, $p3)
{
  global $charset;
  $p2--;
  if (!$charset) { return mb_substr($p1, $p2, $p3); }
  return mb_substr($p1, $p2, $p3,$charset);
}

$db->sqliteCreateFunction('substr', 'sqlite_ufunc_mb_substr', 3);
lower


2.xの例
sqlite_create_function ($db, 'lower',  'sqlite_ufunc_mb_lower');

 function sqlite_ufunc_mb_lower($str)
 {
  global $charset;
  if (!$charset)
    {
    return mb_strtolower($str);
    }
   else
    {
    return mb_strtolower($str, $charset);
    }
 }
sql関数との引数の違いを注意しながら
mb_関数で 置換していくと
そのまま使えるようになるかもしれないです。
注:利用する文字コードの指定を間違わないように適時変更してください。