mysql字符集為utf8時(shí)incorrect string value異常

先說編碼

1.為什么需要編碼

  • 計(jì)算機(jī)是英語國(guó)家做出來的玩意兒亏较,所以它只能認(rèn)識(shí)英文字母符號(hào)担败,阿拉伯?dāng)?shù)字等贯要,其他所有的語言要想在計(jì)算機(jī)中顯示就必須進(jìn)行編碼,翻譯冬念。而計(jì)算機(jī)中表示數(shù)據(jù)信息的最小單元為一個(gè)字節(jié)趁窃,即8個(gè)bit,可以表示0~255個(gè)字符急前,世界上的各種語言千奇百怪醒陆,需要多個(gè)字節(jié)來表示,多個(gè)字節(jié)表示的數(shù)據(jù)要存儲(chǔ)在計(jì)算機(jī)中或者要在網(wǎng)絡(luò)間傳輸必須轉(zhuǎn)化為計(jì)算機(jī)認(rèn)識(shí)的byte裆针,如何轉(zhuǎn)換刨摩,那就是編碼寺晌。

2.常見的編碼

  • ascii碼

只占用一個(gè)字節(jié),第一位為0澡刹,用低7位來編碼呻征。

  • ISO-88591系列(ISO-88591-1 到ISO-88591-15)

這玩意兒也只占用一個(gè)字節(jié),歐洲一些國(guó)家的字符用ascii沒法表示出來罢浇,所以把a(bǔ)scii編碼的首位也用來編碼陆赋。

  • GB2312

可以表示漢字的編碼,兩個(gè)字節(jié)(兼容ascii碼單字節(jié)形式)己莺,能表示6000多個(gè)漢字(區(qū)位碼奏甫,高位字節(jié)存當(dāng)前漢字在哪個(gè)區(qū),低位字節(jié)存在區(qū)中第幾位)凌受,相對(duì)算是挺少的阵子。。胜蛉。

其實(shí)在GB2312編碼里挠进,并不是所有的字符都會(huì)用兩個(gè)字節(jié)來表示的。為了能清晰說明這個(gè)這個(gè)問題誊册,我用二進(jìn)制編碼來解釋一下领突。 首先,ASCII編碼雖然說是用一個(gè)字節(jié)來表示字符案怯,但是它其實(shí)只用了后7位君旦,第1位永遠(yuǎn)是0。它的編碼范圍嘲碱,從00000000到01111111金砍,都是以0開頭的。 而GB2312編碼麦锯,就是在ASCII編碼的基礎(chǔ)上進(jìn)行擴(kuò)充的恕稠,它規(guī)定了:ASCII的字符完整地包含在GB2312里,編碼不變扶欣,仍然是以0開頭鹅巍,用一個(gè)字節(jié)來表示一個(gè)字符;對(duì)于ASCII沒有的字符料祠,就用1開頭來區(qū)分骆捧,用兩個(gè)字節(jié)合起來表示一個(gè)字符。 這樣髓绽,在解碼的時(shí)候凑懂,遇到字節(jié)是以0開頭的,就知道這一個(gè)字節(jié)就表示了一個(gè)字符梧宫;遇到字節(jié)是以1開頭的接谨,就知道要加上下一個(gè)字節(jié)合起來表示一個(gè)字符摆碉。這樣就在GB2312中既把ASCII的字符包含了進(jìn)來,又能將它們區(qū)分出來脓豪,能達(dá)到兼容的效果了巷帝。

  • GBK

也是兩個(gè)字節(jié),但是可以表示的漢字有幾萬個(gè)扫夜,完全是在gb2312的基礎(chǔ)上進(jìn)行擴(kuò)展楞泼,完全兼容GB2312編碼,所以基本可以忘記有GB2312這個(gè)編碼笤闯,直接使用GBK替代他就好堕阔。
tips:其實(shí)gbk和GB2312也算是變長(zhǎng)編碼,根據(jù)碼表查詢到碼位置大小來進(jìn)行判斷用一個(gè)字節(jié)還是雙字節(jié)颗味。

=====================
以下兩個(gè)unicode編碼的實(shí)現(xiàn)..

  • UTF-16

tips:最最最高效的編碼

他的編碼理念是常見的普通字符都用兩個(gè)字節(jié)存儲(chǔ)超陆,而且不需要查碼表什么的,因?yàn)閡nicode規(guī)范里浦马,世界上任何一個(gè)符號(hào)时呀,字符都有一個(gè)唯一編碼對(duì)應(yīng),所以u(píng)tf-16直接將對(duì)應(yīng)的字符的unicode編碼分別放在兩個(gè)字節(jié)里即可晶默,單字節(jié)就能表示的字符它以高位補(bǔ)0方式存儲(chǔ)谨娜。(以不同的處理器可能會(huì)以高位在前或者高位在后的方式解析,所以編碼解碼時(shí)需要指定到底是高位在前還是低位在前)

對(duì)于UCS-4輔助平面內(nèi)的字符磺陡,采用四字節(jié)存儲(chǔ):
Unicode碼位值為2AEAB趴梢,減去0x10000得到1AEAB(二進(jìn)制值為0001 1010 1110 1010 1011),前10位加上D800得到D86B币他,后10位加上DC00得到DEAB坞靶。于是該字的UTF-16編碼值為D86BDEAB(該值為大端表示,小端為6BD8ABDE)圆丹。

  • UTF-8

任何一個(gè)程序員都知道的屌屌的編碼,它對(duì)utf-16編碼的優(yōu)化:
1.變長(zhǎng)編碼躯喇,高位為0辫封,表示它是一個(gè)ascii編碼字符(ascii編碼本身就是首位為0,所以u(píng)tf-8完全兼容ascii編碼)廉丽,大于一個(gè)字節(jié)的編碼倦微,會(huì)在首位以連續(xù)的1的個(gè)數(shù)來標(biāo)識(shí)是幾個(gè)字節(jié)。后面字節(jié)的前兩位一律設(shè)為10正压。
2.變長(zhǎng)帶來的好處:相對(duì)節(jié)約空間欣福,安全(utf-16是每?jī)蓚€(gè)挨著的字節(jié)表示一個(gè)字符,如果在網(wǎng)絡(luò)上傳輸時(shí)丟失了某個(gè)字節(jié)焦履,那么該字節(jié)后面的所有編碼將會(huì)全部亂掉拓劝,而utf-8編碼將不會(huì)應(yīng)用后續(xù)的編碼)

uftf-8編碼示意:

Unicode符號(hào)范圍 | UTF-8編碼方式
(十六進(jìn)制) | (二進(jìn)制)
--------------------+---------------------------------------------
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.編碼選擇

  • 首先雏逾,中文不能使用ascii或者iso系列,單字節(jié)編碼不可以表示中文
  • 其次郑临,gbk和gb2312必須選gbk栖博,不用多說(現(xiàn)在已經(jīng)流行GB18030了)
  • utf-8和utf-16之間:java虛擬機(jī)使用utf-16進(jìn)行編碼,追求編碼解碼速度(字節(jié)固定好查找)厢洞,字節(jié)只在機(jī)器本地的磁盤和內(nèi)存之間流動(dòng)仇让,不走網(wǎng)絡(luò),所以也不會(huì)丟失躺翻。 在web開發(fā)中utf-8是最佳最佳選擇丧叽,空間小,容錯(cuò)率高~~~

4.深入javaweb書上提到的關(guān)鍵點(diǎn)

  • 看一段字符到底會(huì)占多少內(nèi)存公你,不是看string.length有多長(zhǎng)踊淳,而是要看其用的什么編碼,(作者提到他在做項(xiàng)目時(shí)對(duì)cookie做過各種壓縮省店,但是最終并木有任何成效)

  • java的string使用的編碼是unicode嚣崭,但是,當(dāng)string存在于內(nèi)存中時(shí)(也就是當(dāng)程序運(yùn)行時(shí)懦傍、你在代碼中用string類型的引用對(duì)它進(jìn)行操作時(shí)雹舀、也就是string沒有被存在文件中且也沒有在網(wǎng)絡(luò)中傳輸(序列化)時(shí)),是“只有編碼而沒有編碼格式的”粗俱,所以java程序中的任何String對(duì)象说榆,說它是gbk還是utf-8都是錯(cuò)的,gbk和utf-8是編碼格式而不是編碼寸认,String在內(nèi)存中不需要“編碼格式”(記住編碼格式是在存文件或序列化的時(shí)候使用的), 它只是一個(gè)unicode的字符串而已

作者:溫悅
鏈接:https://www.zhihu.com/question/20361462/answer/14899233
來源:知乎
著作權(quán)歸作者所有签财。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處偏塞。

5.書中的其他知識(shí)點(diǎn)(與本文的東西無關(guān))

http請(qǐng)求的編碼:

  • 首先唱蒸,urlPath路徑上的字符一般是通過utf-8編碼,而querystring中則是使用gbk編碼(為什么url中的中文編碼后會(huì)有一大堆%灸叼,因?yàn)閡rl編碼規(guī)范規(guī)定轉(zhuǎn)換為16進(jìn)制表示后需要在每個(gè)16進(jìn)制位前加上%)神汹,<b>瀏覽器對(duì)url中path和query的編碼不一樣</b>
  • tomcat對(duì)path的編碼定義<Connector URIEncoding="UTF-8"/>這里不定義將默認(rèn)采用iso編碼。
  • 對(duì)于query的編碼解碼古今,是在后端代碼中調(diào)用request.getParameter("xx")時(shí)進(jìn)行的屁魏。解碼時(shí)會(huì)先查看http header的contenttype重定義的charset(需要在tomcat重配置<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>)
    tips:<b>在平時(shí)的開發(fā)中,盡量避免在uri或者path中傳中文捉腥,很容易亂碼氓拼,因?yàn)椴煌臑g覽器編碼規(guī)則不一樣,后端開發(fā)者沒辦法完全掌控</b>
  • 對(duì)于header的編碼解碼,沒地方可以配置桃漾,所以http header不支持傳入中文坏匪,一定要傳入中文需要先編碼。解碼觸發(fā)時(shí)機(jī)為request.getHeader("xx"),解碼方式固定以:iso
  • 對(duì)于http postbody內(nèi)容呈队,瀏覽器端會(huì)先根據(jù)contentType設(shè)置的charset來進(jìn)行編碼剥槐,服務(wù)端接收到數(shù)據(jù)也將會(huì)以contentType中的charset進(jìn)行解碼。

utf-8再稍微多說一點(diǎn)

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

從上個(gè)小節(jié)得出結(jié)論宪摧,utf-8這種變長(zhǎng)的表示方式其實(shí)可以表示8個(gè)字節(jié)以內(nèi)的大小的編碼(因?yàn)樗且允鬃帜傅倪B續(xù)1的個(gè)數(shù)來判斷是幾個(gè)字節(jié)的)粒竖,其實(shí)utf-8在目前的最大可表示字節(jié)數(shù)為4.對(duì)應(yīng)如下:

ddddd.png

數(shù)據(jù)庫的字符集utf8與utf8mb4

使用show variables like ‘%character%’;可以查看mysql當(dāng)前的字符集。

  • utf8:標(biāo)準(zhǔn)的 UTF-8 字符集編碼是可以用 1~4 個(gè)字節(jié)去編碼21位字符几于,這幾乎包含了是世界上所有能看見的語言了蕊苗。然而在MySQL里實(shí)現(xiàn)的utf8最長(zhǎng)使用3個(gè)字節(jié),也就是只支持到了 Unicode 中的U+0000至U+FFFF)沿彭,包含了控制符朽砰、拉丁文,中喉刘、日瞧柔、韓等絕大多數(shù)國(guó)際字符。重要的事情說三遍:<b>特智能表示3個(gè)字節(jié)以內(nèi)的編碼字符</b>,<b>特智能表示3個(gè)字節(jié)以內(nèi)的編碼字符</b>,<b>特智能表示3個(gè)字節(jié)以內(nèi)的編碼字符</b>

  • utf8mb4:新版本的mysql才支持睦裳,簡(jiǎn)單說 utf8mb4 是 utf8 的超集并完全兼容utf8嘶炭,能夠用四個(gè)字節(jié)存儲(chǔ)更多的字符谓娃。(當(dāng)然這樣空間會(huì)比較浪費(fèi)一點(diǎn))

如果當(dāng)前db的字符集為utf8盐数,插入的數(shù)據(jù)為4個(gè)字節(jié)編碼時(shí)慨畸,將會(huì)拋出: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

插入數(shù)據(jù)庫的數(shù)據(jù)是一堆mysql建表語句,為什么會(huì)出現(xiàn)4個(gè)字節(jié)編碼的字符

  • 寫了個(gè)main方法蛛蒙,從磁盤讀入這個(gè)ddl.sql文件糙箍,挨個(gè)字符讀取并判斷其編碼字節(jié)數(shù),最終找到這樣一串字符(因?yàn)橹苯诱迟N過來就變樣了牵祟,所以截圖)
dd.png

這里的第四個(gè)字符編碼為4位的(我本來還以為是ddl.sql文件里有四個(gè)字節(jié)的utf-8編碼才能表示的中文漢子呢~~~~~~~~~)深夯,原來是因?yàn)榻ū碚Z句的comment里有亂碼,而這些亂碼里有4字節(jié)utf-8字符诺苹。

  • 為什么從公司內(nèi)部的db導(dǎo)出的建表與存在這樣奇怪的亂碼咕晋。(還沒搞清楚。筝尾。捡需。办桨。)

解決方式

  • 修改數(shù)據(jù)庫字符集為utf8mb4筹淫,在java連接mysql時(shí)加上set names utf8mb4;,但是公司的dba很拽的,db字符集不支持自助修改损姜,dba整天整天的沒反應(yīng)饰剥。。摧阅。汰蓉。。蛋疼
  • 在java端過濾掉這些4字節(jié)字符(這種亂碼本來就沒啥用棒卷,過濾掉豈不更好顾孽。。比规。~~)

正則表達(dá)式之unicode匹配

都知道正則表達(dá)式可以用[a-zA-Z]這樣的方式匹配字母若厚,那我們的中文字符也想這樣匹配怎么辦呢,用unicode匹配吧蜒什,世界上任何一個(gè)字符都可以用unicode來表示测秸。

  • \u開頭表示直接匹配unicode編碼
2E80~33FFh:中日韓符號(hào)區(qū)。收容康熙字典部首灾常、中日韓輔助部首霎冯、注音符號(hào)、日本假名钞瀑、韓文音符沈撞,中日韓的符號(hào)、標(biāo)點(diǎn)仔戈、帶圈或帶括符文數(shù)字关串、月份,以及日本的假名組合监徘、單位晋修、年號(hào)、月份凰盔、日期墓卦、時(shí)間等。  
  
3400~4DFFh:中日韓認(rèn)同表意文字?jǐn)U充A區(qū)户敬,總計(jì)收容6,582個(gè)中日韓漢字落剪。  
  
4E00~9FFFh:中日韓認(rèn)同表意文字區(qū),總計(jì)收容20,902個(gè)中日韓漢字尿庐。  
  
A000~A4FFh:彝族文字區(qū)忠怖,收容中國(guó)南方彝族文字和字根。  
  
AC00~D7FFh:韓文拼音組合字區(qū)抄瑟,收容以韓文音符拼成的文字凡泣。  
  
F900~FAFFh:中日韓兼容表意文字區(qū),總計(jì)收容302個(gè)中日韓漢字。  
  
FB00~FFFDh:文字表現(xiàn)形式區(qū)鞋拟,收容組合拉丁文字骂维、希伯來文、阿拉伯文贺纲、中日韓直式標(biāo)點(diǎn)航闺、小符號(hào)、半角符號(hào)猴誊、全角符號(hào)等潦刃。  
  • -p表示匹配unicode編碼的屬性(unicode屬性有很多,類似中文標(biāo)點(diǎn)懈叹,漢字什么什么的)

\p{xx}表示一個(gè)有屬性 xx 的字符,可以在左花括號(hào) { 后面增加 ^ 表示取反福铅。比如: \p{^Lu} 就等同于 \P{Lu}。

  • -P表示匹配沒有unicode編碼的屬性
    \P{xx}表示一個(gè)沒有屬性 xx 的字符

最后项阴,用unicode正則表達(dá)式替換掉所有4個(gè)字節(jié)的utf-8編碼字符

前面講過utf-8的3個(gè)字節(jié)以內(nèi)能表示的字符的unicode編碼范圍滑黔,所以直接過濾掉不在這個(gè)范圍的字符

public static final String filterCodeLargerThan3Byte(String s) {

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

        return s.replaceAll("[^\\u0000-\\u007F\\u0080-\\u07FF\\u0800-\\uFFFF]", "");
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市环揽,隨后出現(xiàn)的幾起案子略荡,更是在濱河造成了極大的恐慌,老刑警劉巖歉胶,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汛兜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡通今,警方通過查閱死者的電腦和手機(jī)粥谬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辫塌,“玉大人漏策,你說我怎么就攤上這事【拾保” “怎么了掺喻?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)储矩。 經(jīng)常有香客問我感耙,道長(zhǎng),這世上最難降的妖魔是什么持隧? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任即硼,我火速辦了婚禮,結(jié)果婚禮上屡拨,老公的妹妹穿的比我還像新娘只酥。我一直安慰自己题诵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布层皱。 她就那樣靜靜地躺著,像睡著了一般赠潦。 火紅的嫁衣襯著肌膚如雪叫胖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天她奥,我揣著相機(jī)與錄音瓮增,去河邊找鬼。 笑死哩俭,一個(gè)胖子當(dāng)著我的面吹牛绷跑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凡资,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼砸捏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了隙赁?” 一聲冷哼從身側(cè)響起垦藏,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伞访,沒想到半個(gè)月后掂骏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厚掷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年弟灼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冒黑。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡田绑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抡爹,到底是詐尸還是另有隱情辛馆,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布豁延,位于F島的核電站昙篙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏诱咏。R本人自食惡果不足惜苔可,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袋狞。 院中可真熱鬧焚辅,春花似錦映屋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至湾蔓,卻和暖如春瘫析,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背默责。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工贬循, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桃序。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓杖虾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親媒熊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奇适,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容