字符编码的奇怪结果

时间:2016-02-02 12:19:35

标签: java encoding utf-8

以下是情景 -

  • DB2数据库位于大型机系统(z / OS)
  • Web服务器在USS(z / OS的Unix部分)上运行,使用Spring JDBC运行Java代码
  • 我们测试的浏览器和Windows 7上运行的客户端程序(默认编码为windows-1252)

我们有一个包含西班牙语字符(ú)的字符串,它使用Spring的JDBCTemplate存储在数据库中,所以基本上是JDBC。

  • 当使用JDBC客户端(Squirrel,用Java编写)查询时,它显示为其他内容(ú)。
  • 当使用示例JDBC程序查询并将结果打印为字符串时,它会显示为其他内容(ú)。
  • 当使用示例JDBC程序查询并将结果打印为UTF-8编码的字符串[new String( str ,“UTF-8”)]时,它会正确显示(ú)。
  • 使用此-Dfile.encoding = utf-8以UTF-8编码启动JVM时,结果将在上述两种情况下打印为其他内容(ú)。
  • 运行应用程序前端的浏览器也将其显示为Ã,但HTML的内容标题设置为UTF-8。

在这个阶段我有点困惑并且有这些问题 -

  • 如果以UTF-8格式打印字符串特别有用,为什么在使用UTF-8编码启动JVM时它不起作用。
  • 问题实际可能发生在哪一层,数据库或JVM?

我应该在应用程序级别而不是列级别解决这个问题?

任何指针都会有所帮助。

1 个答案:

答案 0 :(得分:5)

您所看到的效果都可以通过假设数据以UTF-8字节写入数据库,但数据库认为这些字节是其他字符集(ISO-LATIN-1或Windows-1252),然后当您读取数据时,您获得的字符串是那些被解释为ISO-LATIN-1或相关字符集的字节。

UTF-8中的字符ú是两个字节0xC3 0xBA。当这些字节被解释为ISO-LATIN-1或win-1252时,您将获得两个字符ú

以UTF-8编写的两个字符ú是四个字节0​​xC3 0x83 0xC2 0xBA。当这四个字节被解释为ISO-LATIN-1(或win-1252)时,您将获得四个字符ú

(Windows-1252和ISO-LATIN-1恰好就所讨论的所有字节/字符达成一致,所以根据证据我无法区分它们之间的差异)

我相信,你发生了什么事:

  1. JDBC客户端正在查询您的数据库,并从数据库中获取包含两个字符ú的字符串。

  2. 当JVM将结果打印到Windows 7控制台框时,如果不是-Dfile.encoding=utf-8启动,它会向控制台框发送表示win-1252中的字符串。如果JVM 以该选项启动,它会向控制台框发送以UTF-8表示字符串所需的字节。

  3. 您的Windows 7控制台框设置为windows-1252,并通过解释java根据windows-1252发送的字节来显示java打印出的内容

  4. 当您在没有参数的情况下调用.getBytes()时,您正在使用JVM的默认编码将字符串转换为字节。因此,如果默认JVM编码为UTF-8,new String(str.getBytes(), "UTF-8")将生成相同的字符串,并且如果默认编码与UTF-8不同,则只会导致实际发生的事情。

  5. 这解释了您提供的所有证据:JDBC检索的java字符串包含字符ú,然后当非utf-8 JVM尝试将其打印到控制台框时,将打印为{ {1}}。当utf-8 JVM尝试将此字符串打印到控制台框时,它会输出四个字节0​​xC3 0x83 0xC2 0xBA,并且控制台会将其解释为四个字符ú。当java Web服务器尝试将此字符串发送回浏览器时,它会这样做 - 浏览器看到的是java应用程序从JDBC接收的内容。

    要检查的第一件事是Spring JDBCTemplate正确接收数据并正确写入数据库。你能让Spring记录它从浏览器收到的内容,并确保浏览器发送UTF-8,并且Spring知道浏览器正在发送UTF-8吗? (你可能想要检查一件事是记录接收到的字符串以及每个字段中字符串的长度。这可以让你知道事情是否被正确解释为UTF-8)

    假设数据正确进入数据库,并且正如您所说无法在数据库端进行更改,并且希望仅从应用程序端进行更改,则可以对从JDBC接收的每个字符串执行此操作:

    ú

    无论JVM的默认编码是什么,都应该将您的字符串转换回您想要的字符串。

    为了将来参考,使用new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8) 从Windows命令行运行jvm通常需要首先更改控制台上的代码页,以便正确查看内容。 (这可以使用命令-Dfile.encoding=utf-8来完成。只需记住在运行没有设置该选项的JVM命令之前使用chcp 65001进行更改。