字符串截取乱码?都是编码惹的祸!

举报
你是猴子请来的救兵吗 发表于 2022/12/09 20:14:50 2022/12/09
【摘要】 有小伙伴在使用substring截取中文字符串时,发现返回的是乱码,该怎么办呢?一文帮你分析清楚,让你不再迷茫!

引言

有小伙伴在使用substring截取中文字符串时,发现返回的是乱码,就很困惑;看个例子,下图SQL的本意是从第五个字符开始截取两个字符,但事与愿违~

select substring('xian华为666', 5, 2);

image.png

这种问题不止一个小伙伴有在实践中遇到过,因此带大家一起剖析下问题所在。

截取函数

GaussDB(DWS)中截取字符串的函数主要有三个:substrb()、substr()、substring() ,以及他们截取单位的差异,不了解的小伙伴可以看下这个帖子:GaussDB(DWS)中的字符截取三胞胎【这次高斯不是数学家】

说回上面的案例,我们知道 substring 是按字符截取,但为什么会输出乱码?

数据库编码

场景1:服务端SQL_ASCII + 客户端UTF8

这是因为数据库的编码原因,在编码为SQL_ASCII的数据库中,数据是不区分编码,按字节流存的。因此 substring(‘xian华为666’, 5, 2) 所代表的含义是从第五个字节开始截取2个字节的长度。而实际截取到的结果字节,与原始字符编码有关。

在UTF8编码中,中文为三个字节,“华为”表示为E58D8E E4B8BA,最终截取结果为两个字节,因此不能正常表示一个中文字符。

-- UTF8编码的原始字符的16进制表示如下
postgres=# select hex('xian华为666');
            hex
----------------------------
 7869616EE58D8EE4B8BA363636
(1 row)

--因此实际截取到的是两个字节,而不是两个字符
postgres=# select hex(substring('xian华为666', 5, 2));
 hex
------
 E58D
(1 row)

场景2:服务端SQL_ASCII + 客户端GBK

在GBK编码中,中文为两个字节,“华为”表示为BBAA CEAA,最终截取结果为两个字节,刚好可以表示一个中文字符“华”。

-- GBK编码的原始字符的16进制表示如下
postgres=# select hex('xian华为666');
           hex
-------------------------
 7869616EBBAACEAA363636
(1 row)

--因此实际截取到的是两个字节,而不是两个字符
postgres=# select hex(substring('xian华为666', 5, 2));
 hex
------
 BBAA
(1 row)

场景3:服务端UTF8 + 客户端UTF8

服务端指定为UTF8时,会根据UTF8编码区分字符,所以substring表现为字符的截取。最终截取结果为两个字符“华为”。

--因此实际截取到的是两个字符,也就是6个字节
postgres=# select hex(substring('xian华为666', 5, 2));
     hex
--------------
 E58D8EE4B8BA
(1 row)

场景4:服务端UTF8 + 客户端GBK

只要服务端指定了具体的编码如UTF8时,即使客户端编码与服务端编码不一致,在服务端会在数据处理前统一转换为当前编码即UTF8,因此最终截取结果仍为两个字符“华为”,与场景3保持一致。

--因此实际截取到的是两个字符,仍然是6个字节
postgres=# select hex(substring('xian华为666', 5, 2));
     hex
--------------
 E58D8EE4B8BA
(1 row)

知识小结

通过以上一番解析之后,相信大家对字符截取的异常结果的处理就可以手到擒来了。

回过头来看下,文章开头的乱码问题应该怎么处理呢?

1,数据库编码SQL_ASCII不变,修改截取的长度为需要的字节数长度,此时注意区分源数据编码。

-- 源数据为UTF8编码
postgres=# select substring('xian华为666', 5, 6);
 substring
-----------
 华为
(1 row)

-- 源数据为GBK编码
postgres=# select substring('xian华为666', 5, 4);
 substring
-----------
 华为
(1 row)

2,切换到编码为UTF8的数据库,即可按照字符数截取。

-- 按照字符个数截取
postgres=# select substring('xian华为666', 5, 2);
 substring
-----------
 华为
(1 row)

想了解GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的PB级数仓黑科技~

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。