Java 奇怪的字符编码结果

Java 奇怪的字符编码结果,java,encoding,utf-8,Java,Encoding,Utf 8,下面是一个场景- DB2数据库位于大型机系统(z/OS)上 web服务器运行在USS(z/OS的Unix部分)上,通过SpringJDBC运行Java代码 我们测试的浏览器和运行在Windows 7上的客户端程序(默认编码为Windows-1252) 我们有一个包含西班牙语字符(ú)的字符串,它使用Spring的JDBCTemplate存储在数据库中,因此本质上是JDBC 当使用JDBC客户机(Squirrel,用Java编写)查询时,它显示为其他内容(ú) 当使用示例JDBC程序查询并

下面是一个场景-

  • DB2数据库位于大型机系统(z/OS)上
  • web服务器运行在USS(z/OS的Unix部分)上,通过SpringJDBC运行Java代码
  • 我们测试的浏览器和运行在Windows 7上的客户端程序(默认编码为Windows-1252)
我们有一个包含西班牙语字符(ú)的字符串,它使用Spring的JDBCTemplate存储在数据库中,因此本质上是JDBC

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

  • 如果以UTF-8格式打印字符串特别有效,那么当JVM以UTF-8编码启动时为什么不起作用呢
  • 问题实际发生在哪一层,数据库还是JVM
我应该在应用程序级别而不是列级别解决什么问题


任何指针都会有帮助。

您看到的效果都可以通过以下假设来解释:数据以UTF-8字节的形式写入数据库,但数据库认为这些字节是其他字符集(ISO-LATIN-1或Windows-1252),然后在读取数据时,返回的字符串是那些解释为ISO-LATIN-1或相关字符集的字节

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

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

(Windows-1252和ISO-LATIN-1恰好在所有有问题的字节/字符上都一致,因此从证据来看,我无法区分它们之间的区别)

我相信你现在的处境是:

  • JDBC客户端正在查询您的数据库,并从数据库中返回一个包含两个字符的字符串

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

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

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

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

    首先要检查的是SpringJDBCTemplate是否正确接收数据并正确写入数据库。您能否让Spring将它从浏览器接收到的内容记录到某个地方,并确保浏览器正在发送UTF-8,并且Spring知道浏览器正在发送UTF-8?(有一件事您可能需要检查日志,记录接收到的字符串以及每个字段中的字符串长度。这可以让您知道是否正确地将内容解释为UTF-8)

    假设数据正确地进入数据库,并且正如您所说的,您不能在数据库端进行更改,而只希望从应用程序端进行更改,那么您可以对从JDBC接收到的每个字符串执行以下操作:

    new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)
    
    无论JVM的默认编码是什么,这都应该将字符串转换回您想要的内容


    为了便于将来参考,从windows命令行运行带有
    -Dfile.encoding=utf-8
    的jvm通常需要先更改控制台上的代码页,才能正确查看内容。(这可以通过命令
    chcp 65001
    完成。只需记住在运行没有该选项集的JVM命令之前使用
    chcp 1252
    进行更改即可)

    您使用的字符串的双参数构造函数将字节数组作为第一个参数。在调用新字符串(str,“UTF-8”)之前,您如何将数据库中的字符串结果转换为字节?@Daniel:我对从数据库中获取的字符串调用str.getBytes()。在JDBC中,您必须指定编码您已经这样做了吗?可能数据库已经损坏了。UTF-8可能已写入数据库,但数据库编码设置为不同的值:Never call
    str.getBytes()