【问题标题】:String with encoding from MYSQL DB Query comes through as mangled in Java带有来自 MYSQL DB Query 编码的字符串在 Java 中被破坏
【发布时间】:2015-08-11 05:01:09
【问题描述】:

带有 MYSQL DB Query 编码的字符串在 Java 中被破坏

我使用 Java 连接到一个 MYSQL 数据库,在该数据库中查询一个表以进行收集 一个字段。

该字段有UTF8编码的文本,即:

Córas Éireann

上面的文字是我登录MYSQL控制台看的时候看到的 表中的那一行。部署的mysql版本为: mysql Ver 14.14 Distrib 5.1.52,用于使用 readline 5.1 的 unknown-linux-gnu (x86_64)

如果我使用 python 程序连接到同一个数据库并查询相同 表并获得相同的行,文本看起来正确,即它来自 Córas Éireann

但是,当我通过 Java 查询时,文本显示为:

Córas Éireann

我怀疑是西方(ISO latin1),但我不确定,只是猜测。

我做了一个显示表状态,发现我正在查询的表有排序规则 utf8_general_ci

我查询的表没有任何额外的编码定义, 字段名称是 varchar(512)。

+--------------------+--------------+------+-----+---------+----------------+
| Field              | Type         | Null | Key | Default | Extra          |
+--------------------+--------------+------+-----+---------+----------------+
| id                 | int(11)      | NO   | PRI | NULL    | auto_increment |
| name               | varchar(512) | YES  |     | NULL    |                |

我导入的SQL connect JAR是mysql-connector-java-5.1.36,我也试过v 5.1.34 和 5.0.8 但没有区别。

这就是我连接数据库的方式:

    String dbStr =
    String.format("jdbc:mysql://%s:%d/%s?useUnicode=yes&characterEncoding=UTF-8", LOCAL_MYSQL_HOST,
                                       LOCAL_MYSQL_PORT, LOCAL_MYSQL_DB);
    try {
        cdb = DriverManager.getConnection(dbStr, LOCAL_MYSQL_USER, LOCAL_MYSQL_PASS);
        Statement dbStatement = cdb.createStatement();
        String query = String.format("SELECT name FROM customer WHERE id=%d",customerId);
        ResultSet row = dbStatement.executeQuery(query);
        if (row.first()) {
             System.out.println("name is " + row.getString("name");
        }
    } catch (SQLException exc) {
        exc.printStackTrace();
    }

请注意,我的原始实现不包含

?useUnicode=yes&characterEncoding=UTF-8

但添加它并没有使它变得更好 或更糟。我添加了它,因为我认为这可能是 罪魁祸首。我还尝试了 latin1 而不是 utf-8 来看看这是否 差异但没有运气,结果完全相同,即它出来为 Córas Éireann.

我什至尝试过这样的事情:

                byte[] rowBytes = row.getBytes("name");
                String utfdecocedStr = new String(rowBytes, "UTF-8");
                System.out.println(utfdecocedStr);

但输出仍为 Córas Éireann

在python中,我不做任何解码/编码,查询和连接都是 基本的,我得到了正确的字符串。我是否缺少需要的步骤 在 DB 或 Java 端完成以使其正常工作?在 my.conf 中,我没有 有任何设置来设置任何编码配置。

Python 方法:

  import MySQLdb
  cdb = MySQLdb.connect(host=LOCAL_MYSQL_HOST,port=LOCAL_MYSQL_PORT,
                         user=LOCAL_MYSQL_USER,
                         passwd=LOCAL_MYSQL_PASS,db=LOCAL_MYSQL_DB)
  ccursor = self.cdb.cursor()
  query = """SELECT name FROM customer WHERE id='%d' """%(customer_id)
  row = ccursor.execute(query)
  if row:
      customername = ccursor.fetchone()

谢谢你...Amro

---------更新20150811---------------

我在数据库上运行了以下命令,发现以下配置可能解释了我所看到的行为:

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

mysql> show variables like 'collation%';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | utf8_general_ci   |
| collation_database   | latin1_swedish_ci |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)

通过将以下内容添加到 my.cnf:

character-set-server = utf8
character-set-filesystem = utf8

然后表格演变为:

mysql> 显示 'character%' 等变量;

+--------------------------+---------------------------------------------+
| Variable_name            | Value                                       |
+--------------------------+---------------------------------------------+
| character_set_client     | utf8                                        |
| character_set_connection | utf8                                        |
| character_set_database   | latin1                                      |
| character_set_filesystem | utf8                                        |
| character_set_results    | utf8                                        |
| character_set_server     | utf8                                        |
| character_set_system     | utf8                                        |
| character_sets_dir       | /usr/share/percona-xtradb-cluster/charsets/ |
+--------------------------+---------------------------------------------+


+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | utf8_general_ci   |
| collation_database   | latin1_swedish_ci |
| collation_server     | utf8_general_ci   |
+----------------------+-------------------+

所以我在 DB 控制台上发出了命令:

 ALTER DATABASE <dbname> CHARACTER SET utf8;

显示像'character%'这样的变量;

+--------------------------+---------------------------------------------+
| Variable_name            | Value                                       |
+--------------------------+---------------------------------------------+
| character_set_client     | utf8                                        |
| character_set_connection | utf8                                        |
| character_set_database   | utf8                                        |
| character_set_filesystem | utf8                                        |
| character_set_results    | utf8                                        |
| character_set_server     | utf8                                        |
| character_set_system     | utf8                                        |
| character_sets_dir       | /usr/share/percona-xtradb-cluster/charsets/ |
+--------------------------+---------------------------------------------+
8 rows in set (0.00 sec)

mysql> show variables like 'collation%';
+----------------------+-----------------+
| Variable_name        | Value           |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database   | utf8_general_ci |
| collation_server     | utf8_general_ci |
+----------------------+-----------------+

很遗憾,这仍然没有解决问题。

有人可以告诉我如何在不清除数据库的情况下纠正这个问题吗?

【问题讨论】:

  • 首先,您需要确保在存储字段时,您使用的是相同的 UTF-8 格式。数据库本身的数据是否损坏?您可以尝试在new String(rowBytes, "ISO-8859-1") 中使用ISO-8859-1。但是你在哪里得到这个作为输出Córas Éireann 在你的服务器控制台或你的 IDE 控制台上?
  • 您如何查看 java 中的输出?如果这是通过 Eclipse,您是否可以尝试在 Run -> Run Configurations -> Common 更改 Eclipse 控制台的编码
  • @UtKarash,数据库中的数据很好,正如我指出的,如果我通过连接到 mysql 控制台或 python 查看数据,编码会正确显示。所以数据库数据没有损坏。我使用 IntelliJ 作为我的 IDE,并将其设置为 UTF-8。输出 Córas Éireann 被转储到我的服务器控制台(终端窗口)上,我从中运行我的 java 程序和我的 python 程序。
  • 你似乎在做所有正确的事情。可以检查一下:stackoverflow.com/questions/5405236/… 在上面的链接中,my.cnf 用于 linux,my.ini 用于 Windows。在这些文件中添加字符集default-character-set=utf8 有时可以解决问题。
  • @UTKarash,谢谢,我会试一试,让你知道。我必须看看是否需要重新启动数据库以强制重新读取 my.conf。数据库部署在 linux 机器上。

标签: java mysql utf-8 character-encoding


【解决方案1】:

首先,我意识到我使用的表具有 UTF-8 编码,但输入它的源表是 latin1,这证实了我对我认为的编码的怀疑。

编写代码将数据从一个复制到另一个的人没有进行编码转换,所以我觉得到那时数据已经损坏了编码。

我进行了各种实验,包括使用

连接到数据库
?useUnicode=yes&characterEncoding=UTF-8

我还在 SQL 控制台上玩过以下内容:

SET character_set_client=latin1;
SET character_set_connection=latin1;
SET character_set_database=latin1;
SET character_set_results=latin1;

以及使用 my.cnf 和设置的变体:

[mysqld] 
character-set-server = utf8 
character-set-filesystem = utf8

无论如何,这些都没有帮助。所以最后为了证明这不是一个 java 问题,而是一个损坏的数据集,我用 UTF8 编码创建了自己的表,将名称存储在其中,并让我的程序提取数据。它看起来是正确的。所以现在是修复原始表格内容的问题。

我唯一无法解释的是 python 程序如何没有将此标记为问题。这不是我第一次遇到 Python 对 Java 强类型的宽恕,这在这些情况下是福也是祸。

================================

2015/08/19 更新:

当我将源表固定为 UTF8 并正确存储数据时,Java 代码可以工作,但 python 代码坏了。

为了在 python 中修复它,我只是添加了

self.cdb = MySQLdb.connect(host=LOCAL_MYSQL_HOST,port=LOCAL_MYSQL_PORT,
                         user=LOCAL_MYSQL_USER, 
  passwd=LOCAL_MYSQL_PASS,
  db=LOCAL_MYSQL_DB,use_unicode=True,charset="UTF8")

这解决了它。

不幸的是,当我早些时候尝试使用 Java 驱动程序解决这个问题时,它不起作用:

dbStr = String.format("jdbc:mysql://%s:%d/%s?useUnicode=yes&characterEncoding=latin1", esConfig.LOCAL_MYSQL_HOST, esConfig.LOCAL_MYSQL_PORT, esConfig.LOCAL_MYSQL_DB);

我用的是java 1.7.80,jdbc包是mysql-connector-java-5.1.36-bin.jar,是我从Oracle找到的最新的

【讨论】:

    【解决方案2】:

    你有 Mojibake

    • 客户端中的字节已正确编码为 utf8(良好)。
    • 您可能默认连接到SET NAMES latin1(或set_charset('latin1') 或...)。 (应该是utf8。)
    • xx 表中的列被声明为CHARACTER SET latin1。 (或者它可能是从表/数据库继承的。)(应该是utf8。)
    • 表格中的列可能是CHARACTER SET utf8,也可能不是,但应该是这样。

    如果您需要修复数据,则需要“2 步 ALTER”,类似于

    ALTER TABLE Tbl MODIFY COLUMN col VARBINARY(...) ...;
    ALTER TABLE Tbl MODIFY COLUMN col VARCHAR(...) ... CHARACTER SET utf8 ...;
    

    【讨论】:

    • 感谢反馈,我之前尝试过修改目标表,但Java端看不出有什么不同。我担心的是源表在 latin1 中,而我们复制到的目标表是 UTF-8。将两个表都设为 UTF-8 产生了我想要的一致结果。
    • 你是如何“复制”的?一种可行的方法是将latin1翻译成utf8;另一种方式会破坏字节。
    • 不幸的是,我没有关于副本是如何发生的信息。另一个人用了java,没有任何翻译就复制了。但是,正如我们现在一样,Java 默认为 UTF-16,所以我不确定这是否导致了这个问题。它没有被检测到,因为使用 python 或 SQL 控制台我们从未看到任何损坏
    • 我认为Java 内部使用utf16或ucs2,但外部呈现utf8。 Córas Éireann 涉及 latin1,而不是 utf16/ucs2。如需进一步讨论,您能否提供SELECT col, HEX(col) FROM tbl WHERE ... 以查看实际存储的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多