一、編碼進(jìn)程
【01編碼】——很久很久以前,為了表示二極管的通、分,我們引入的高電平、低電平,之后又引入的1、0編碼進(jìn)行代替。
【ASCII編碼】——很久以前,也就是上個(gè)世紀(jì)60年代,美國(guó)佬為了把計(jì)算機(jī)的“0101010”編碼與文字進(jìn)行對(duì)應(yīng)起來(lái),制定了一套ASCII編碼方案。人總是自私的,他只對(duì)自己的語(yǔ)言進(jìn)行編碼,26個(gè)字母、數(shù)字、其他符號(hào),只用了7位二進(jìn)制數(shù)搞定,第一位用0表示,預(yù)留著。所以ASCII編碼最多127編碼。
【“二代”ASCII編碼】——不單單是美國(guó)佬想把計(jì)算機(jī)語(yǔ)言和英文聯(lián)系起來(lái),其他國(guó)家也想把自己的語(yǔ)言關(guān)聯(lián)起來(lái),我法文、俄文就基于ASCII編碼,利用上ASCII第一位(未使用)變成1,來(lái)對(duì)自己國(guó)家的語(yǔ)言進(jìn)行編碼。
介樣,每一個(gè)國(guó)家都基于ASCII,讓第一位變成1,完成對(duì)本國(guó)的語(yǔ)言的編碼。由于他們各干各的,沒(méi)有溝通,從而導(dǎo)致了同一種編碼出現(xiàn)不同的文字。
【gb2312碼】——我們偉大的祖國(guó)80年代也開(kāi)始對(duì)漢字進(jìn)行編碼,由于我們的文字較多,即使是基于ASCII將第一位變成1,也不夠我們使用(國(guó)語(yǔ)博大精深拉),所以就制定了一套gb2312編碼,使用2個(gè)字節(jié)表示。
【BIG5編碼】——我國(guó)的港澳地區(qū),他們是使用繁體字(gb2312編碼最初并沒(méi)有考慮到繁體字),怎么辦了?他們就出了自己的區(qū)域編碼BIG5
【中國(guó)一統(tǒng)GBK編碼】——為了統(tǒng)一漢字,迫切的需要設(shè)計(jì)出一種既能支持簡(jiǎn)體字又能表示繁體的新編碼方案,GBK誕生了!他兼容了絕大部分gb2312編碼(gb2312編碼的文字用gbk可以讀出來(lái),但是不兼容BIG5編碼)
【世界大統(tǒng)unicode編碼】——各國(guó)編碼各做各的,總不是意見(jiàn)好事,為了便于交流,國(guó)際社會(huì)引入了unicode——(uni統(tǒng)一的意思,code編碼)把所有國(guó)家的文字都進(jìn)行了編碼。
【統(tǒng)一后浪費(fèi)空間問(wèn)題】——統(tǒng)一是一件好事,但是也是有問(wèn)題的。英文只要1個(gè)字節(jié)ASCII編碼就可以表示,你unicode還需要2個(gè)字節(jié)或更多,導(dǎo)致unicode表示英文的時(shí)候前面有很多無(wú)用的000000000000000000,對(duì)吧。
【解決問(wèn)題,UTF編碼】——解決方法是使用utf8編碼,它是基于unicode編碼上的一種優(yōu)化,英文使用1個(gè)字節(jié),中文使用2、3個(gè)(絕大部分是3個(gè),個(gè)別有看到2、4個(gè)的一般不用)字節(jié)。好處:我能屈能伸,我可以變化長(zhǎng)度來(lái)保存,就不會(huì)浪費(fèi)空間了吧。
來(lái)看看unicode和utf對(duì)應(yīng)吧
<!--[if !supportLists]-->· <!--[endif]-->0000 0000-0000 007F | 0xxxxxxx
<!--[if !supportLists]-->· <!--[endif]-->0000 0080-0000 07FF | 110xxxxx 10xxxxxx
<!--[if !supportLists]-->· <!--[endif]-->0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
<!--[if !supportLists]-->· <!--[endif]-->0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
舉例說(shuō)明:
“漢”字的Unicode編碼是0x6C49。0x6C49在0x0800-0xFFFF之間,使用用3字節(jié)模板了:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫(xiě)成二進(jìn)制是:0110 1100 0100 1001,用這個(gè)比特流依次代替模板后面的x,得到:11100110 10110001 10001001,十六進(jìn)制E6 B1 89,轉(zhuǎn)換成10進(jìn)制就是230 這個(gè)就是我們的UTF8編碼,"漢".getBytes("utf-8") 得到-26 -79 -119,是負(fù)數(shù),也是跟這里有關(guān)
漢 Unicode utf8
0x6c49 0xe6b189
二、java編程的轉(zhuǎn)碼
1、JVM內(nèi)存中保存的文字編碼
從源文件àclass文件過(guò)程中的轉(zhuǎn)碼情況。最終的class文件都是以u(píng)nicode編碼的,我們前面所做的工作就是把各種不同的編碼轉(zhuǎn)換為unicode編碼,比如從GBK轉(zhuǎn)換為unicode,從UTF-8轉(zhuǎn)換為unicode。因?yàn)橹挥胁捎谜_的編碼來(lái)轉(zhuǎn)碼才能保證不出現(xiàn)亂碼。Jvm在運(yùn)行時(shí)其內(nèi)部都是采用unicode編碼的,其實(shí)在輸出時(shí),又會(huì)做一次編碼的轉(zhuǎn)換。
比如:Sysout.out.println(“我們”)。經(jīng)過(guò)正確的解碼后”我們”是unicode保存在內(nèi)存中的,但是在向標(biāo)準(zhǔn)輸出(控制臺(tái))輸出時(shí),jvm又做了一次轉(zhuǎn)碼,它會(huì)采用操作系統(tǒng)默認(rèn)編碼(中文操作系統(tǒng)是GBK),將內(nèi)存中的unicode編碼轉(zhuǎn)換為GBK編碼,然后輸出到控制臺(tái)。
參考文獻(xiàn):http://www.blogjava.net/zhangchao/archive/2011/05/26/351051.html
2、愛(ài)也getBytes,恨也getBytes
一般我們是通過(guò)getBytes來(lái)進(jìn)行編碼,如下
public byte[] getBytes(String charsetName) //使用規(guī)定的編碼
public byte[] getBytes(Charset charset) //使用規(guī)定的編碼
public byte[] getBytes() //使用啟動(dòng)JVM設(shè)置的編碼(如果沒(méi)有設(shè)置一般是系統(tǒng)默認(rèn)編碼),避免使用,默認(rèn)編碼和平臺(tái)有關(guān)
一般我們通過(guò)new String來(lái)進(jìn)行解碼,如下
public String(byte bytes[], String charsetName)
public String(byte bytes[], Charset charset)
public String(byte bytes[])
原則1:使用某一種編碼集對(duì)文字進(jìn)行編碼,就得使用同一種編碼集進(jìn)行解碼
比如:“漢”是在java文件中的,此java文件是utf-8或者gbk編碼的
getBytes使用utf-8編碼,new String使用utf-8解密
new String("漢".getBytes("utf-8"), "utf-8"));
原則2:看清楚對(duì)方給你的是什么編碼,使用逆過(guò)程進(jìn)行編碼、解碼(像出棧操作),最終得到你想要文字
比如:對(duì)方進(jìn)行了如下操作,將“漢”進(jìn)行utf-8編碼,然后使用iso8859-1進(jìn)行解碼生成對(duì)應(yīng)的文字:,最后通過(guò)文本發(fā)送給你。對(duì)方可能的操作應(yīng)該是:
String x = new String("漢".getBytes("utf-8"), "iso8859-1");
我們需要對(duì)文本中文字x:進(jìn)行iso8859-1編碼,得到的是“漢”utf-8的譯碼,然后我們?cè)趯?duì)“漢”進(jìn)行解碼得到其utf8的文字
new String(x.getBytes("iso8859-1"),"utf-8"));
原則3:慎用getBytes(),他是基于平臺(tái)的,你得看看JVM啟動(dòng)的時(shí)候file.encoding這個(gè)屬性值到底是什么(請(qǐng)注意tomcat的啟動(dòng)JVM時(shí)候設(shè)置的編碼)
源碼:特別注意黃底部分
staticbyte[] encode(char[] ca, int off, int len) {
String csn = Charset.defaultCharset().name();
try {
return encode(csn, ca, off, len);
} catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn);
}
try {
return encode("ISO-8859-1", ca, off, len);
} catch (UnsupportedEncodingException x) {
// If this code is hit during VM initialization, MessageUtils is
// the only way we will be able to get any kind of error message.
MessageUtils.err("ISO-8859-1 charset not available: "
+ x.toString());
// If we can not find ISO-8859-1 (a required encoding) then things
// are seriously wrong with the installation.
System.exit(1);
returnnull;
}
publicstatic Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
java.security.PrivilegedAction pa =
new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
defaultCharset = cs;
else
defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
}
-Dfile.encoding解釋:
在命令行中輸入java,在給出的提示中會(huì)出現(xiàn)-D的說(shuō)明:
-D<name>=<value>
set a system property
-D后面需要跟一個(gè)鍵值對(duì),作用是通過(guò)命令行向java虛擬機(jī)傳遞一項(xiàng)系統(tǒng)屬性
對(duì)-Dfile.encoding=UTF-8來(lái)說(shuō)就是設(shè)置系統(tǒng)屬性file.encoding為UTF-8
在java.nio.charset包中的Charset.java中。這段話的意思說(shuō)的很明確了,簡(jiǎn)單說(shuō)就是默認(rèn)字符集是在java虛擬機(jī)啟動(dòng)時(shí)決定的,后面無(wú)法動(dòng)態(tài)改變了。默認(rèn)字符集就是從file.encoding這個(gè)屬性中獲取的。
Java's file.encoding property on Windows platform
This property is used for the default encoding in Java, all readers and writers would default to using this property. file.encoding is set to the default locale of Windows operationg system since Java 1.4.2. System.getProperty("file.encoding") can be used to access this property. Code such as System.setProperty("file.encoding", "UTF-8") can be used to change this property. However, the default encoding can be not changed dynamically even this property can be changed. So the conclusion is that the default encoding can't change after JVM starts. java -dfile.encoding=UTF-8 can be used to set the default encoding when starting a JVM. I have searched for this option Java official documentation. But I can't find it.
參考文獻(xiàn):http://www.cnblogs.com/vigarbuaa/archive/2012/04/11/2442582.html
三、url網(wǎng)址的編碼規(guī)范
一、url編碼規(guī)范
一般來(lái)說(shuō),URL只能使用英文字母、阿拉伯?dāng)?shù)字和某些標(biāo)點(diǎn)符號(hào),不能使用其他文字和符號(hào)。比如,世界上有英文字母的網(wǎng)址“http://www.abc.com”,但是沒(méi)有希臘字母的網(wǎng)址“http://www.aβγ.com”(讀作阿爾法-貝塔-伽瑪.com)。這是因?yàn)榫W(wǎng)絡(luò)標(biāo)準(zhǔn)RFC 1738做了硬性規(guī)定:
"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."
“只有字母和數(shù)字[0-9a-zA-Z]、一些特殊符號(hào)“$-_.+!*'(),”[不包括雙引號(hào)]、以及某些保留字,才可以不經(jīng)過(guò)編碼直接用于URL。”
這意味著,如果URL中有漢字,就必須編碼后使用。但是麻煩的是,RFC 1738沒(méi)有規(guī)定具體的編碼方法,而是交給應(yīng)用程序(瀏覽器)自己決定。這導(dǎo)致“URL編碼”成為了一個(gè)混亂的領(lǐng)域。
參考文獻(xiàn):http://www.ruanyifeng.com/blog/2010/02/url_encoding.html
四、瀏覽器的編碼
下面就讓我們看看,“URL編碼”到底有多混亂。我會(huì)依次分析四種不同的情況,在每一種情況中,瀏覽器的URL編碼方法都不一樣。把它們的差異解釋清楚之后,我再說(shuō)如何用Javascript找到一個(gè)統(tǒng)一的編碼方法。
情況1:網(wǎng)址路徑中包含漢字
打開(kāi)IE(我用的是8.0版),輸入網(wǎng)址“http://zh.wikipedia.org/wiki/春節(jié)”。注意,“春節(jié)”這兩個(gè)字此時(shí)是網(wǎng)址路徑的一部分。
查看HTTP請(qǐng)求的頭信息,會(huì)發(fā)現(xiàn)IE實(shí)際查詢的網(wǎng)址是“http://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82”。也就是說(shuō),IE自動(dòng)將“春節(jié)”編碼成了“%E6%98%A5%E8%8A%82”。
我們知道,“春”和“節(jié)”的utf-8編碼分別是“E6 98 A5”和“E8 8A 82”,因此,“%E6%98%A5%E8%8A%82”就是按照順序,在每個(gè)字節(jié)前加上%而得到的。(具體的轉(zhuǎn)碼方法,請(qǐng)參考我寫(xiě)的《字符編碼筆記》。)
在Firefox中測(cè)試,也得到了同樣的結(jié)果。所以,結(jié)論1就是,網(wǎng)址路徑的編碼,用的是utf-8編碼。
情況2:查詢字符串包含漢字
在IE中輸入網(wǎng)址“http://www.baidu.com/s?wd=春節(jié)”。注意,“春節(jié)”這兩個(gè)字此時(shí)屬于查詢字符串,不屬于網(wǎng)址路徑,不要與情況1混淆。
查看HTTP請(qǐng)求的頭信息,會(huì)發(fā)現(xiàn)IE將“春節(jié)”轉(zhuǎn)化成了一個(gè)亂碼。
切換到十六進(jìn)制方式,才能清楚地看到,“春節(jié)”被轉(zhuǎn)成了“B4 BA BD DA”。
我們知道,“春”和“節(jié)”的GB2312編碼(我的操作系統(tǒng)“Windows XP”中文版的默認(rèn)編碼)分別是“B4 BA”和“BD DA”。因此,IE實(shí)際上就是將查詢字符串,以GB2312編碼的格式發(fā)送出去。
Firefox的處理方法,略有不同。它發(fā)送的HTTP Head是“wd=%B4%BA%BD%DA”。也就是說(shuō),同樣采用GB2312編碼,但是在每個(gè)字節(jié)前加上了%。
所以,結(jié)論2就是,查詢字符串的編碼,用的是操作系統(tǒng)的默認(rèn)編碼。(我使用谷歌瀏覽器,他會(huì)是使用utf-8編碼的)
情況3:Get方法生成的URL包含漢字
前面說(shuō)的是直接輸入網(wǎng)址的情況,但是更常見(jiàn)的情況是,在已打開(kāi)的網(wǎng)頁(yè)上,直接用Get或Post方法發(fā)出HTTP請(qǐng)求。
根據(jù)臺(tái)灣中興大學(xué)呂瑞麟老師的試驗(yàn),這時(shí)的編碼方法由網(wǎng)頁(yè)的編碼決定,也就是由HTML源碼中字符集的設(shè)定決定。
<meta http-equiv="Content-Type" content="text/html;charset=xxxx">
如果上面這一行最后的charset是UTF-8,則URL就以UTF-8編碼;如果是GB2312,URL就以GB2312編碼。
舉例來(lái)說(shuō),百度是GB2312編碼,Google是UTF-8編碼。因此,從它們的搜索框中搜索同一個(gè)詞“春節(jié)”,生成的查詢字符串是不一樣的。
百度生成的是%B4%BA%BD%DA,這是GB2312編碼。
Google生成的是%E6%98%A5%E8%8A%82,這是UTF-8編碼。
所以,結(jié)論3就是,GET和POST方法的編碼,用的是網(wǎng)頁(yè)的編碼。
參考文獻(xiàn):http://www.ruanyifeng.com/blog/2010/02/url_encoding.html
五、網(wǎng)頁(yè)、js編碼
情況4:Ajax調(diào)用的URL包含漢字
前面三種情況都是由瀏覽器發(fā)出HTTP請(qǐng)求,最后一種情況則是由Javascript生成HTTP請(qǐng)求,也就是Ajax調(diào)用。還是根據(jù)呂瑞麟老師的文章,在這種情況下,IE和Firefox的處理方式完全不一樣。
舉例來(lái)說(shuō),有這樣兩行代碼:
url = url + "?q=" +document.myform.elements[0].value; // 假定用戶在表單中提交的值是“春節(jié)”這兩個(gè)字
http_request.open('GET', url, true);
那么,無(wú)論網(wǎng)頁(yè)使用什么字符集,IE傳送給服務(wù)器的總是“q=%B4%BA%BD%DA”,而Firefox傳送給服務(wù)器的總是“q=%E6%98%A5%E8%8A%82”。也就是說(shuō),在Ajax調(diào)用中,IE總是采用GB2312編碼(操作系統(tǒng)的默認(rèn)編碼),而Firefox總是采用utf-8編碼。這就是我們的結(jié)論4。
六、Javascript函數(shù):escape()
好了,到此為止,四種情況都說(shuō)完了。
假定前面你都看懂了,那么此時(shí)你應(yīng)該會(huì)感到很頭痛。因?yàn)椋瑢?shí)在太混亂了。不同的操作系統(tǒng)、不同的瀏覽器、不同的網(wǎng)頁(yè)字符集,將導(dǎo)致完全不同的編碼結(jié)果。如果程序員要把每一種結(jié)果都考慮進(jìn)去,是不是太恐怖了?有沒(méi)有辦法,能夠保證客戶端只用一種編碼方法向服務(wù)器發(fā)出請(qǐng)求?
回答是有的,就是使用Javascript先對(duì)URL編碼,然后再向服務(wù)器提交,不要給瀏覽器插手的機(jī)會(huì)。因?yàn)?/span>Javascript的輸出總是一致的,所以就保證了服務(wù)器得到的數(shù)據(jù)是格式統(tǒng)一的。
Javascript語(yǔ)言用于編碼的函數(shù),一共有三個(gè),最古老的一個(gè)就是escape()。雖然這個(gè)函數(shù)現(xiàn)在已經(jīng)不提倡使用了,但是由于歷史原因,很多地方還在使用它,所以有必要先從它講起。
實(shí)際上,escape()不能直接用于URL編碼,它的真正作用是返回一個(gè)字符的Unicode編碼值。比如“春節(jié)”的返回結(jié)果是%u6625%u8282,也就是說(shuō)在Unicode字符集中,“春”是第6625個(gè)(十六進(jìn)制)字符,“節(jié)”是第8282個(gè)(十六進(jìn)制)字符。
它的具體規(guī)則是,除了ASCII字母、數(shù)字、標(biāo)點(diǎn)符號(hào)“@ * _ + - . /”以外,對(duì)其他所有字符進(jìn)行編碼。在\u0000到\u00ff之間的符號(hào)被轉(zhuǎn)成%xx的形式,其余符號(hào)被轉(zhuǎn)成%uxxxx的形式。對(duì)應(yīng)的解碼函數(shù)是unescape()。
所以,“Hello World”的escape()編碼就是“Hello%20World”。因?yàn)榭崭竦?/span>Unicode值是20(十六進(jìn)制)。
還有兩個(gè)地方需要注意。
首先,無(wú)論網(wǎng)頁(yè)的原始編碼是什么,一旦被Javascript編碼,就都變?yōu)?/span>unicode字符。也就是說(shuō),Javascipt函數(shù)的輸入和輸出,默認(rèn)都是Unicode字符。這一點(diǎn)對(duì)下面兩個(gè)函數(shù)也適用。
其次,escape()不對(duì)“+”編碼。但是我們知道,網(wǎng)頁(yè)在提交表單的時(shí)候,如果有空格,則會(huì)被轉(zhuǎn)化為+字符。服務(wù)器處理數(shù)據(jù)的時(shí)候,會(huì)把+號(hào)處理成空格。所以,使用的時(shí)候要小心。
七、Javascript函數(shù):encodeURI()
encodeURI()是Javascript中真正用來(lái)對(duì)URL編碼的函數(shù)。
它著眼于對(duì)整個(gè)URL進(jìn)行編碼,因此除了常見(jiàn)的符號(hào)以外,對(duì)其他一些在網(wǎng)址中有特殊含義的符號(hào)“; / ? : @ & = + $ , #”,也不進(jìn)行編碼。編碼后,它輸出符號(hào)的utf-8形式,并且在每個(gè)字節(jié)前加上%。
它對(duì)應(yīng)的解碼函數(shù)是decodeURI()。
需要注意的是,它不對(duì)單引號(hào)'編碼。
八、Javascript函數(shù):encodeURIComponent()
最后一個(gè)Javascript編碼函數(shù)是encodeURIComponent()。與encodeURI()的區(qū)別是,它用于對(duì)URL的組成部分進(jìn)行個(gè)別編碼,而不用于對(duì)整個(gè)URL進(jìn)行編碼。
因此,“; / ? : @ & = + $ , #”,這些在encodeURI()中不被編碼的符號(hào),在encodeURIComponent()中統(tǒng)統(tǒng)會(huì)被編碼。至于具體的編碼方法,兩者是一樣。
它對(duì)應(yīng)的解碼函數(shù)是decodeURIComponent()。
參考文獻(xiàn):http://www.ruanyifeng.com/blog/2010/02/url_encoding.html
九、tomcat容器的編碼
1、啟動(dòng)tomcat容器時(shí)的編碼
在第二節(jié)中,說(shuō)道:慎用getBytes(),他是基于平臺(tái)的,tomcat中JVM啟動(dòng)的時(shí)候file.encoding這個(gè)屬性值到底是什么,由誰(shuí)決定?
Linux修改catalina.sh文件
JAVA_OPTS=”-server -Dfile.encoding=GBK -Xms=512m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m -verbose:gc -Xloggc:${CATALINA_HOME}/logs/gc.log`date +%Y-%m-%d-%H-%M` -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -noclassgc”
如果你像上面這樣告訴JVM,啟動(dòng)我tomcat容器的時(shí)候,使用GBK來(lái)編碼,后續(xù)的getBytes返回的就是GBK編碼的東西,這點(diǎn)特別在部署的tomcat的時(shí)候注意。
2、tomcat對(duì)表單中g(shù)et提交的數(shù)據(jù),出亂碼看哪里?
get提交的tomcat從4.x之后不是使用request.setCharacterEncoding("字符集"),從這里獲取,它直接使用直接對(duì)參數(shù)進(jìn)行編碼,這個(gè)編碼他是從tomcat的配置文件中獲取<Connector port="8080" protocol="HTTP/1.1" maxThreads="150" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>
3、tomcat對(duì)表單中post提交的數(shù)據(jù),出亂碼看哪里?
對(duì)于post需要在web.xml里面配置攔截器,主要是在程序第一次從request里面獲取參數(shù)之前把request給設(shè)定了具體的編碼
publicvoid doFilter(ServletRequest req, ServletResponse resp,FilterChain chain)throws IOException, ServletException {
req.setCharacterEncoding(encoding);//這里是過(guò)濾器,直接設(shè)置編碼
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
ActionContext.setContext(request, response);
chain.doFilter(req,resp);
ActionContext.removeContext();
}
還有一點(diǎn)需要注意的是:你在web.xml里面配置過(guò)濾器的優(yōu)先級(jí)是不是最高,一定要在request出現(xiàn)之前攔截。
4、tomcat對(duì)jsp頁(yè)面的編碼,看哪里?
你還記得jsp頁(yè)面中有下面這句話么?pageEncoding="utf-8"告訴tomcat,你編譯我jsp文件的時(shí)候使用他來(lái)編譯,如果這句話沒(méi)有,他默認(rèn)使用charset=utf-8"這句話來(lái)編譯,同時(shí)注意保存jsp的時(shí)候使用編碼也應(yīng)該與pageEncoding="utf-8"保持一致
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
你還記得下面這句話么?由他來(lái)告訴瀏覽器使用什么編碼的
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
十、mysql數(shù)據(jù)庫(kù)的編碼
1、查看數(shù)據(jù)庫(kù)編碼
需要以root用戶身份登陸才可以查看數(shù)據(jù)庫(kù)編碼方式(以root用戶身份登陸的命令為:
>mysql -u root –p,之后兩次輸入root用戶的密碼),查看數(shù)據(jù)庫(kù)的編碼方式命令為:
>show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
從以上信息可知數(shù)據(jù)庫(kù)的編碼為latin1,需要修改為gbk或者是utf8;
其中,character_set_client為客戶端編碼方式;character_set_connection為建立連接使用的編碼;character_set_database數(shù)據(jù)庫(kù)的編碼;
character_set_results結(jié)果集的編碼;
character_set_server數(shù)據(jù)庫(kù)服務(wù)器的編碼;
只要保證以上四個(gè)采用的編碼方式一樣,就不會(huì)出現(xiàn)亂碼問(wèn)題。
2、更改數(shù)據(jù)庫(kù)編碼
停止MySQL的運(yùn)行
/etc/init.d/mysql start (stop) 為啟動(dòng)和停止服務(wù)器
MySQL主配置文件為my.cnf,一般目錄為/etc/mysql
var/lib/mysql/ 放置的是數(shù)據(jù)庫(kù)表文件夾,這里的mysql相當(dāng)于windows下mysql的date文件夾
當(dāng)我們需要修改MySQL數(shù)據(jù)庫(kù)的默認(rèn)編碼時(shí),需要編輯my.cnf文件進(jìn)行編碼修改,在linux下修改mysql的配置文件my.cnf,文件位置默認(rèn)/etc/my.cnf文件
找到客戶端配置[client] 在下面添加
default-character-set=utf8 默認(rèn)字符集為utf8
在找到[mysqld] 添加
default-character-set=utf8 默認(rèn)字符集為utf8
init_connect='SET NAMES utf8' (設(shè)定連接mysql數(shù)據(jù)庫(kù)時(shí)使用utf8編碼,以讓mysql數(shù)據(jù)庫(kù)為utf8運(yùn)行)
3、改了之后,為什么win里面cmd中的那個(gè)console控制臺(tái)還是顯示亂碼了?
Mysql通過(guò)客戶端發(fā)送到控制臺(tái)展示之前是utf8,但是console是使用系統(tǒng)的默認(rèn)編碼(gbk),所以在中文的時(shí)候發(fā)生了亂碼顯示,但是不影響程序操作,只是顯示的時(shí)候有問(wèn)題。
如何解決了?
方法一:(只對(duì)當(dāng)前窗口有用,推薦使用)
方法二:將控制臺(tái)展示編碼改成utf8(對(duì)所有的窗口,不推薦)
1、打開(kāi)CMD.exe命令行窗口
2、通過(guò) chcp命令改變代碼頁(yè),UTF-8的代碼頁(yè)為65001
F:\trash>chcp 65001
執(zhí)行該操作后,代碼頁(yè)就被變成UTF-8了。但是,在窗口中仍舊不能正確顯示UTF-8字符。
3、修改窗口屬性,改變字體
在命令行標(biāo)題欄上點(diǎn)擊右鍵,選擇"屬性"->"字體",將字體修改為T(mén)rue Type字體"Lucida Console",然后點(diǎn)擊確定將屬性應(yīng)用到當(dāng)前窗口。
4、通過(guò)以上操作并不能完全解決問(wèn)題,因?yàn)轱@示出來(lái)的內(nèi)容有可能不完全。可以先最小化,然后最大化命令行窗口,文件的內(nèi)容就完整的顯示出來(lái)了。
Java開(kāi)發(fā)中,常常會(huì)遇到亂碼的問(wèn)題,一旦遇到這種問(wèn)題,常常就很扯蛋,每個(gè)人都不愿意承認(rèn)是自己的代碼有問(wèn)題。其實(shí)編碼問(wèn)題并沒(méi)有那么神秘,那么不可捉摸,搞清Java的編碼本質(zhì)過(guò)程就真相大白了。
先看個(gè)圖:
其實(shí),編碼問(wèn)題存在兩個(gè)方面:JVM之內(nèi)和JVM之外。
1、Java文件編譯后形成class
這里Java文件的編碼可能有多種多樣,但Java編譯器會(huì)自動(dòng)將這些編碼按照J(rèn)ava文件的編碼格式正確讀取后產(chǎn)生class文件,這里的class文件編碼是Unicode編碼(具體說(shuō)是UTF-16編碼)。
因此,在Java代碼中定義一個(gè)字符串:
String s="漢字";
不管在編譯前java文件使用何種編碼,在編譯后成class后,他們都是一樣的----Unicode編碼表示。
2、JVM中的編碼
JVM加載class文件讀取時(shí)候使用Unicode編碼方式正確讀取class文件,那么原來(lái)定義的String s="漢字";在內(nèi)存中的表現(xiàn)形式是Unicode編碼。
當(dāng)調(diào)用String.getBytes()的時(shí)候,其實(shí)已經(jīng)為亂碼買(mǎi)下了禍根。因?yàn)榇朔椒ㄊ褂闷脚_(tái)默認(rèn)的字符集來(lái)獲取字符串對(duì)應(yīng)的字節(jié)數(shù)組。在WindowsXP中文版中,使用的默認(rèn)編碼是GBK,不信運(yùn)行下:
public class Test {
public static void main(String[] args) {
System.out.println( "當(dāng)前JRE:" + System.getProperty( "java.version"));
System.out.println( "當(dāng)前JVM的默認(rèn)字符集:" + Charset.defaultCharset());
}
}
當(dāng)前JRE:1.6.0_16
當(dāng)前JVM的默認(rèn)字符集:GBK
當(dāng)不同的系統(tǒng)、數(shù)據(jù)庫(kù)經(jīng)過(guò)多次編碼后,如果對(duì)其中的原理不理解,就容易導(dǎo)致亂碼。因此,在一個(gè)系統(tǒng)中,有必要對(duì)字符串的編碼做一個(gè)統(tǒng)一,這個(gè)統(tǒng)一模糊點(diǎn)說(shuō),就是對(duì)外統(tǒng)一。比如方法字符串參數(shù),IO流,在中文系統(tǒng)中,可以統(tǒng)一使用GBK、GB13080、UTF-8、UTF-16等等都可以,只是要選擇有些更大字符集,以保證任何可能用到的字符都可以正常顯示,避免亂碼的問(wèn)題。(假設(shè)對(duì)所有的文件都用ASCII碼)那么就無(wú)法實(shí)現(xiàn)雙向轉(zhuǎn)換了。
要特別注意的是,UTF-8并非能容納了所有的中文字符集編碼,因此,在特殊情況下,UTF-8轉(zhuǎn)GB18030可能會(huì)出現(xiàn)亂碼,然而一群傻B常常在做中文系統(tǒng)喜歡用UTF-8編碼而不說(shuō)不出個(gè)所以然出來(lái)!最傻B的是,一個(gè)系統(tǒng)多個(gè)人做,源代碼文件有的人用GBK編碼,有人用UTF-8,還有人用GB18030。FK,都是中國(guó)人,也不是外包項(xiàng)目,用什么UTF-8啊,神經(jīng)!源代碼統(tǒng)統(tǒng)都用GBK18030就OK了,免得ANT腳本編譯時(shí)候提示不可認(rèn)的字符編碼。
因此,對(duì)于中文系統(tǒng)來(lái)說(shuō),最好選擇GBK或GB18030編碼(其實(shí)GBK是GB18030的子集),以便最大限度的避免亂碼現(xiàn)象。
3、內(nèi)存中字符串的編碼
內(nèi)存中的字符串不僅僅局限于從class代碼中直接加載而來(lái)的字符串,還有一些字符串是從文本文件中讀取的,還有的是通過(guò)數(shù)據(jù)庫(kù)讀取的,還有可能是從字節(jié)數(shù)組構(gòu)建的,然而他們基本上都不是Unicode編碼的,原因很簡(jiǎn)單,存儲(chǔ)優(yōu)化。
因此就需要處理各種各樣的編碼問(wèn)題,在處理之前,必須明確“源”的編碼,然后用指定的編碼方式正確讀取到內(nèi)存中。如果是一個(gè)方法的參數(shù),實(shí)際上必須明確該字符串參數(shù)的編碼,因?yàn)檫@個(gè)參數(shù)可能是另外一個(gè)日文系統(tǒng)傳遞過(guò)來(lái)的。當(dāng)明確了字符串編碼時(shí)候,就可以按照要求正確處理字符串,以避免亂碼。
在對(duì)字符串進(jìn)行解碼編碼的時(shí)候,應(yīng)該調(diào)用下面的方法:
getBytes(String charsetName)
String( byte[] bytes, String charsetName)
而不要使用那些不帶字符集名稱的方法簽名,通過(guò)上面兩個(gè)方法,可以對(duì)內(nèi)存中的字符進(jìn)行重新編碼。
該文章在 2023/8/18 11:35:49 編輯過(guò)