以下是情景 -
我们有一个包含西班牙语字符(ú)的字符串,它使用Spring的JDBCTemplate存储在数据库中,所以基本上是JDBC。
在这个阶段我有点困惑并且有这些问题 -
我应该在应用程序级别而不是列级别解决这个问题?
任何指针都会有所帮助。
答案 0 :(得分:5)
您所看到的效果都可以通过假设数据以UTF-8字节写入数据库,但数据库认为这些字节是其他字符集(ISO-LATIN-1或Windows-1252),然后当您读取数据时,您获得的字符串是那些被解释为ISO-LATIN-1或相关字符集的字节。
UTF-8中的字符ú
是两个字节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,new String(str.getBytes(), "UTF-8")
将生成相同的字符串,并且如果默认编码与UTF-8不同,则只会导致实际发生的事情。
这解释了您提供的所有证据:JDBC检索的java字符串包含字符ú
,然后当非utf-8 JVM尝试将其打印到控制台框时,将打印为{ {1}}。当utf-8 JVM尝试将此字符串打印到控制台框时,它会输出四个字节0xC3 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
进行更改。