postgresql 如何诊断字符编码问题

ndh0cuux  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(1)|浏览(164)

我在识别一个似乎与Postgres数据库中存在的奇怪字符有关的问题时遇到了麻烦。我正在使用Java从Postgres中提取数据并将其加载到BigQuery中。偶尔我注意到一些值似乎在此过程中没有明显的原因被更改。经过仔细检查,我发现在所有情况下,这个问题似乎都是由我认为不正常的字符引起的。
Postgres数据库编码是UTF-8。Java编码也是UTF-8。
以下是我所看到的一个例子:
我有一个文本字段,其中包含这个值:SÅ‚awomir
如果我运行这个SQL:
select length('SÅ‚awomir')
我得到一个值9,看起来是正确的。但是,如果我将该字符串导出到一个文本文件中,并在HEX编辑器中查看它,(在我的Visual Studio Code中使用HEX编辑器扩展),看起来该字符串的长度是11,而不是9。仔细检查后,第二个和第三个字符由2个十六进制值表示,而不是像其他字符那样仅由一个十六进制值表示。4个HEX值:
C3 85 C2 82
这里是HEX编辑器的屏幕截图,显示了这些字符。
请帮助我理解这些字符是什么,我可以做些什么。它们是有效的UTF-8字符吗?如果是,为什么它们会被Java程序转换,我如何才能阻止这种情况发生?
更新2023-10-31:感谢@Laurenz Albe的回复。这是对发生的事情(以及如何在未来防止它)的解释,但我不确定它是否完全解决了我的问题,因为我没有能力控制将数据插入数据库的上游进程。
我还有一些相关的细节:
我们使用Google Datastream从Postgres中提取数据并将其移动到BigQuery中。当数据到达BigQuery时,它看起来与Postgres中的数据完全相同(这就是我想要的)。实际上,当我使用Java(JDBC)将该值从BigQuery中提取出来并将其插入另一个BigQuery表时,就会出现问题。
我不会像“Insert into... select from.."那样通过一个插入语句来执行此操作。在这种情况下,数据永远不会离开BigQuery。我所做的是首先获取数据并将结果赋给Java变量。然后在第二步中,我将该值插入到另一个BigQuery表中。当我这样做时,目标表中的数据会发生轻微变化,所以我在想办法防止这种情况发生
下面是原始值和移动到另一个表后的值的示例:



下面是我的HEX查看器中相同文件的屏幕截图:



正如您所看到的,该值已经发生了一些变化-新值似乎是c3 85 e2 80 9a
所以我的问题是我如何才能保持原始值?在将数据拉入Java然后将其放回BigQuery的过程中似乎发生了一些事情。我的Java env配置为使用UTF8编码,所以我有点困惑如何才能保持原始值。

vlju58qv

vlju58qv1#

这是一种“双重编码”。
原始的字符串必须是“Sjuavomir”。第二个字母()用UTF-8中的两个字节C582编码。
现在,当UTF-8编码的字符串被插入到数据库中时,有人将PostgreSQL客户端编码设置为单字节编码,可能是LATIN-1。因此,PostgreSQL将两个字节解释为单独的字符:C5是“",82是不可打印的字符,一个名为“此处允许中断”的控制字符。
PostgreSQL将这两个字符转换为服务器编码UTF-8,这将它们转换为您观察到的四个字节。每个字符都由UTF-8中的两个字节表示。
在转换为BigQuery的过程中,“break permitted here”字符被转换为“curly quote”(<$)。必须发生的是:

  • 使用客户端编码LATIN-1提取数据,因此结果为C582
  • 客户端被插入到BigQuery中,客户端编码为WINDOWS-1252,其中82表示花引号
  • BigQuery服务器将 curl 引号转换为ZTF-8,并以E2809A结束

总结如下:

  • PostgreSQL数据库中的原始数据已经损坏,因为导入数据时client_encoding被设置为LATIN1而不是UTF8
  • 在传输到BigQuery的过程中,由于BigQuery客户端编码被设置为WINDOWS-1252,数据被进一步破坏

相关问题