mysql字符集为utf8时incorrect string v

2017-09-10  本文已影响0人  炫迈哥

先说编码

1.为什么需要编码

2.常见的编码

只占用一个字节,第一位为0,用低7位来编码。

这玩意儿也只占用一个字节,欧洲一些国家的字符用ascii没法表示出来,所以把ascii编码的首位也用来编码。

可以表示汉字的编码,两个字节(兼容ascii码单字节形式),能表示6000多个汉字(区位码,高位字节存当前汉字在哪个区,低位字节存在区中第几位),相对算是挺少的。。。

其实在GB2312编码里,并不是所有的字符都会用两个字节来表示的。为了能清晰说明这个这个问题,我用二进制编码来解释一下。 首先,ASCII编码虽然说是用一个字节来表示字符,但是它其实只用了后7位,第1位永远是0。它的编码范围,从00000000到01111111,都是以0开头的。 而GB2312编码,就是在ASCII编码的基础上进行扩充的,它规定了:ASCII的字符完整地包含在GB2312里,编码不变,仍然是以0开头,用一个字节来表示一个字符;对于ASCII没有的字符,就用1开头来区分,用两个字节合起来表示一个字符。 这样,在解码的时候,遇到字节是以0开头的,就知道这一个字节就表示了一个字符;遇到字节是以1开头的,就知道要加上下一个字节合起来表示一个字符。这样就在GB2312中既把ASCII的字符包含了进来,又能将它们区分出来,能达到兼容的效果了。

也是两个字节,但是可以表示的汉字有几万个,完全是在gb2312的基础上进行扩展,完全兼容GB2312编码,所以基本可以忘记有GB2312这个编码,直接使用GBK替代他就好。
tips:其实gbk和GB2312也算是变长编码,根据码表查询到码位置大小来进行判断用一个字节还是双字节。

=====================
以下两个unicode编码的实现..

tips:最最最高效的编码

他的编码理念是常见的普通字符都用两个字节存储,而且不需要查码表什么的,因为unicode规范里,世界上任何一个符号,字符都有一个唯一编码对应,所以utf-16直接将对应的字符的unicode编码分别放在两个字节里即可,单字节就能表示的字符它以高位补0方式存储。(以不同的处理器可能会以高位在前或者高位在后的方式解析,所以编码解码时需要指定到底是高位在前还是低位在前)

对于UCS-4辅助平面内的字符,采用四字节存储:
Unicode码位值为2AEAB,减去0x10000得到1AEAB(二进制值为0001 1010 1110 1010 1011),前10位加上D800得到D86B,后10位加上DC00得到DEAB。于是该字的UTF-16编码值为D86BDEAB(该值为大端表示,小端为6BD8ABDE)。

任何一个程序员都知道的屌屌的编码,它对utf-16编码的优化:
1.变长编码,高位为0,表示它是一个ascii编码字符(ascii编码本身就是首位为0,所以utf-8完全兼容ascii编码),大于一个字节的编码,会在首位以连续的1的个数来标识是几个字节。后面字节的前两位一律设为10。
2.变长带来的好处:相对节约空间,安全(utf-16是每两个挨着的字节表示一个字符,如果在网络上传输时丢失了某个字节,那么该字节后面的所有编码将会全部乱掉,而utf-8编码将不会应用后续的编码)

uftf-8编码示意:

Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
image.png

3.编码选择

4.深入javaweb书上提到的关键点

作者:温悦
链接:https://www.zhihu.com/question/20361462/answer/14899233
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5.书中的其他知识点(与本文的东西无关)

http请求的编码:

utf-8再稍微多说一点

utf-8:https://en.wikipedia.org/wiki/UTF-8#Description

从上个小节得出结论,utf-8这种变长的表示方式其实可以表示8个字节以内的大小的编码(因为它是以首字母的连续1的个数来判断是几个字节的),其实utf-8在目前的最大可表示字节数为4.对应如下:

ddddd.png

数据库的字符集utf8与utf8mb4

使用show variables like ‘%character%’;可以查看mysql当前的字符集。

如果当前db的字符集为utf8,插入的数据为4个字节编码时,将会抛出:incorrect string value异常
nested exception is org.apache.ibatis.exceptions.PersistenceException: \n### Error updating database. Cause: ERR-CODE: [TDDL-4601][ERR_EXECUTOR] Incorrect string value: '\\xF2\\xBC\\xAF\\xBA\\xEF\\xBF...' for column 'config_value' at row 1 More: [http:\/\/middleware.alibaba-inc.com\/faq\/faqByFaqCode.html?faqCode=TDDL-4601]\n### The error may involve com.alibaba.cornerstone.dao.output.OutputAppConfigDAO.addBatch-Inline\n### The error occurred while setting parameters\n### SQL: insert into output_appconfig ( app_name, component_id, config_value, config_type, finished ) values ( ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ? ) , ( ?, ?, ?, ?, ? )\n### Cause: ERR-CODE: [TDDL-4601][ERR_EXECUTOR] Incorrect string value: '\\xF2\\xBC\\xAF\\xBA\\xEF\\xBF...' for column 'config_value' at row 1

插入数据库的数据是一堆mysql建表语句,为什么会出现4个字节编码的字符

dd.png

这里的第四个字符编码为4位的(我本来还以为是ddl.sql文件里有四个字节的utf-8编码才能表示的中文汉子呢~~~~~~~~~),原来是因为建表语句的comment里有乱码,而这些乱码里有4字节utf-8字符。

解决方式

正则表达式之unicode匹配

都知道正则表达式可以用[a-zA-Z]这样的方式匹配字母,那我们的中文字符也想这样匹配怎么办呢,用unicode匹配吧,世界上任何一个字符都可以用unicode来表示。

2E80~33FFh:中日韩符号区。收容康熙字典部首、中日韩辅助部首、注音符号、日本假名、韩文音符,中日韩的符号、标点、带圈或带括符文数字、月份,以及日本的假名组合、单位、年号、月份、日期、时间等。  
  
3400~4DFFh:中日韩认同表意文字扩充A区,总计收容6,582个中日韩汉字。  
  
4E00~9FFFh:中日韩认同表意文字区,总计收容20,902个中日韩汉字。  
  
A000~A4FFh:彝族文字区,收容中国南方彝族文字和字根。  
  
AC00~D7FFh:韩文拼音组合字区,收容以韩文音符拼成的文字。  
  
F900~FAFFh:中日韩兼容表意文字区,总计收容302个中日韩汉字。  
  
FB00~FFFDh:文字表现形式区,收容组合拉丁文字、希伯来文、阿拉伯文、中日韩直式标点、小符号、半角符号、全角符号等。  

\p{xx}表示一个有属性 xx 的字符,可以在左花括号 { 后面增加 ^ 表示取反。比如: \p{^Lu} 就等同于 \P{Lu}。

最后,用unicode正则表达式替换掉所有4个字节的utf-8编码字符

前面讲过utf-8的3个字节以内能表示的字符的unicode编码范围,所以直接过滤掉不在这个范围的字符

public static final String filterCodeLargerThan3Byte(String s) {

        if (s == null) {
            return s;
        }

        return s.replaceAll("[^\\u0000-\\u007F\\u0080-\\u07FF\\u0800-\\uFFFF]", "");
    }
上一篇下一篇

猜你喜欢

热点阅读