JavaでMySQLにinsertしようとすると、


java.sql.SQLException: Incorrect string value: '\xF1\xA5\xA4\xA1\xE2\x86...' for column 'hoge' at row 1 Query: insert into ...

なんてエラーが発生。
例外コード(エラーコード)やSQLStateはこんな感じ。


ErrorCode=1366
SQLState=HY000

一般的にこのエラーは、UTF-8じゃないデータベースにUTF-8な文字を入れようとしたときに起きるらしい。

なので、 my.cnf や MySQL monitor で調べてみたら、


$ mysql -u username -p dbname
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 801
Server version: 5.0.51a-24+lenny4-log (Debian)
 
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
 
mysql> show variables like 'character%';
+--------------------------+----------------------------+
| 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)

ちゃんとUTF-8のDBになってた。
ということは、ほんとにUTF-8じゃない文字をinsertしようとしていたっぽい。

MySQLのデータベースにinsertしようとしていた文字はこんなの。

MySQLのデータベースにinsertしようとしていた文字


+---+
|065|
|921|
+---+

Twitterに投稿した文字をTwitter APIで取得してMySQLにinsertしようとしてた。

Twitterに投稿した文字をTwitter APIで取得してMySQLにinsertしようとしてた

UTF-8で使えない文字ということなので、こういうイケナイ文字は削除orそれなりに変換する方向で対処することにした。

どうやって変換するのが良さそうなのか以下のコード(抜粋)で実験。


private void check(String[] tweets) throws Exception {
  PrintStream out = new PrintStream("mojibake.txt", "UTF-8");
  for(int i=0; i<tweets.length; i++){
    String s = tweets[i];
    print(out, "t0", s);
    print(out, "t1", t1(s));
    print(out, "t2", t2(s));
    print(out, "t3", t3(s));
  }
  out.flush();
  out.close();
}
 
private static String t1(String s) throws Exception {
  byte[] b = s.getBytes("UTF-8");
  String s2 = new String(b, "UTF-8");
  return s2;
}
 
private static String t2(String s) throws Exception {
  StringBuffer sb = new StringBuffer();
  for(int i=0; i<s.length(); i++){
    char c = s.charAt(i);
    sb.append(c);
  }
  return sb.toString();
}
 
private static String t3(String s) throws Exception {
  ByteBuffer bb = ByteBuffer.allocate(256);
  int size = 0;
  for(int i=0; i<s.length(); i++){
    String c = "" + s.charAt(i);
    byte[] b = c.getBytes("UTF-8");
    bb.put(b);
    size += b.length;
  }
  byte[] dst = new byte[size];
  bb.rewind();
  bb.get(dst);
  return new String(dst, "UTF-8");
}
 
private static void print(PrintStream out, String name, String s) throws Exception {
  out.print(name);
  out.print(" : ");
  out.print(s);
  out.print(" : ");
  // String#getBytes した中身を表示
  {
    byte[] b = s.getBytes("UTF-8");
    for(int i=0; i<b.length; i++){
      out.print(b[i]);
      out.print(",");
    }
  }
  // String#charAt#toString#getBytes した中身を表示
  {
    out.print(" : ");
    for(int i=0; i<s.length(); i++){
      byte[] b = ("" + s.charAt(i)).getBytes("UTF-8");
      for(int j=0; j<b.length; j++){
        out.print(b[j]);
        out.print(",");
      }
    }
  }
  out.println();
}

結果を見ると、t3メソッドでクエスチョンマーク「?」(63)に変換するのが良さそう。

結果を見ると、t3メソッドでクエスチョンマーク「?」(63)に変換するのが良さそう
⇒ 出力結果のテキストファイル: 20110112_mojibake.txt

というわけで、こんな感じでMySQLがイヤがるようなイケナイ文字はクエスチョンマーク「?」に変換することにした。

tags: MySQL Java zurazure

Posted by NI-Lab. (@nilab)