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しようとしていた文字はこんなの。
+---+
|065|
|921|
+---+
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)に変換するのが良さそう。
⇒ 出力結果のテキストファイル: 20110112_mojibake.txt
というわけで、こんな感じでMySQLがイヤがるようなイケナイ文字はクエスチョンマーク「?」に変換することにした。
tags: MySQL Java zurazure
Posted by NI-Lab. (@nilab)