SQL Server中有一个排序规则为SQL_Latin1_General_CP1_CS_AS的表。表有一个列varchar(35),排序规则是SQL_Latin1_General_CP1_CS_AS。
列包含字符8f的字符串(十六进制).
见https://www.fileformat.info/info/unicode/char/008f/index.htm根据这个页面,这个字符转换成UTF8应该变成c28f.
当我在Java中读取该列的值并将其转换为UTF-8时,8f被替换为efbfbd. 所以8f丢失了…有点。见https://www.fileformat.info/info/unicode/char/0fffd/index.htm
public static String convertStrToHex(String str) {
byte[] getBytesFromString = str.getBytes(StandardCharsets.UTF_8);
BigInteger bigInteger = new BigInteger(1, getBytesFromString);
String convertedResult = String.format("%X", bigInteger);
return convertedResult;
}
查询表
select BadCol from MyTbl
System.out.println(convertStrToHex(resultSet.getString(1));
我得到的是EFBFBD而不是C28F。
当我声明字符串变量"u008f"并将其转换为UTF-8:
String code="u008f";
System.out.println(convertStrToHex(code);
我得到正确的C28F。
那么,为什么一个变量被正确转换,但在JDBC->RecordSet错误?
使用SQL Server 2017和2019以及JDBC: mssql和jTDS进行测试,结果相同。
我将感激任何帮助!据我所知,JDBC驱动程序是罪魁祸首。但是为什么? ?
十六进制码为8f的字符在Latin-1中不存在。这是一个无效的字符
因此,当转换为UTF-8时,它被替换为替换字符。
替换字符具有Unicode码点U+FFFD。用UTF-8编码后,它变成了EF BF BD。
你是正确的,8f不是一个有效的UTF-8字节。8f也不是有效的拉丁字符。
8f在某些Windows字符集中是有效的字符,这些字符集是ISO 8859-n字符集的超集。您的varchar值可能是Windows-1250、Windows-1251、Windows-1256或Windows-1257值。您必须根据您的用户的语言,或者不太理想的情况下,根据您的软件的默认语言做出假设。
如果可能的话,将JDBC连接设置为使用这些字符集之一。(确切地说,如何做到这一点将取决于您使用的数据库。例如,我相信MySQL允许characterEncoding=windows-1250
作为JDBC URL中的查询参数。
如果不能这样做,则在从数据库读取值时自己进行转换。替换:
resultSet.getString(1)
:
new String(resultSet.getBytes(1), "windows-1250")
new String(resultSet.getBytes(1), "windows-1251")
new String(resultSet.getBytes(1), "windows-1256")
new String(resultSet.getBytes(1), "windows-1257")
Windows-1250适用于中欧和东欧。维基百科说它可以用于波兰语、捷克语、斯洛伐克语、匈牙利语、斯洛文尼亚语、塞尔维亚语、罗马尼亚语、阿尔巴尼亚语和德语文本。
Windows-1251适用于西里尔语言。维基百科说,它可以用于俄语、乌克兰语和白俄罗斯语等。
Windows-1256用于阿拉伯语。
Windows-1257适用于爱沙尼亚语、拉脱维亚语和立陶宛语。