主要內(nèi)容
1. 字符編碼理論簡(jiǎn)述
1.1 ASCII
1.2 ISO8859-1
1.3 Unicode
1.4 GBK
2. 可能發(fā)生的中文亂碼
2.1 中文變問(wèn)號(hào)玫镐,如:???
2.2 中文變奇怪字符一罩,如:?? ?¥? 或者 ??o?
2.3 中文變“復(fù)雜中文”,如:浣犲ソ
2.4 中文變成一堆黑色菱形+問(wèn)號(hào),如:?????
3. Web開發(fā)中涉及到的中文編解碼
3.1 URL中出現(xiàn)的中文
3.2 Form表單中出現(xiàn)的中文
3.3 JSP中涉及的編碼
3.4 文件的上傳和下載中涉及到的中文亂碼
4. 總結(jié)
1. 字符編碼理論簡(jiǎn)述
本文主要是圍繞Web開發(fā)中涉及到的中文編碼這一常見問(wèn)題展開顷帖,包括了對(duì)字符編碼基礎(chǔ)理論的簡(jiǎn)述以及常見幾種編碼標(biāo)準(zhǔn)的介紹逆航。其中包括:ASCII鼎文、ISO8859-1、Unicode因俐、GBK拇惋。下面先對(duì)這些字符編碼集進(jìn)行簡(jiǎn)單的介紹。
1.1 ASCII
ASCII也就是美國(guó)信息交換標(biāo)準(zhǔn)碼抹剩,采用單字節(jié)編碼方案撑帖,但是編碼只用了后七位字節(jié),表示范圍0-127共128個(gè)字符澳眷。ASCII碼相對(duì)于其它編碼也是最早出現(xiàn)的磷仰。從上世紀(jì)60年代提出開始,到1986年最終定型境蔼。
為什么選擇7位編碼?ASCII在最初設(shè)計(jì)的時(shí)候需要至少能表示64個(gè)碼元:包括26個(gè)字母+10個(gè)數(shù)字+圖形標(biāo)示+控制字符灶平,如果用6bit編碼,可擴(kuò)展部分沒(méi)有了箍土,所以至少需要7bit逢享。那么8bit呢?最終也被標(biāo)準(zhǔn)委員會(huì)否定吴藻,原因很簡(jiǎn)單:滿足編碼需求的前提下瞒爬,最小化傳輸開銷。
1.2 ISO8859-1
ISO-8859-1也被稱為L(zhǎng)atin1,使用單字節(jié)8bit編碼侧但,可以表示256個(gè)西歐字符矢空。其隸屬于ISO8859標(biāo)準(zhǔn)的一部分,還有ISO8859-2禀横、ISO8859-3等等屁药。每一種編碼都對(duì)應(yīng)一個(gè)地區(qū)的字符集。比如:ISO8859-1表示西歐字符柏锄,ISO-8859-16表示中歐字符集酿箭,等等。
1.3 Unicode
不管是ASCII還是ISO8859-1趾娃,其編碼范圍都是有局限的缭嫡。而Unicode標(biāo)準(zhǔn)的目標(biāo)就是消除傳統(tǒng)編碼的局限性。
這里的局限性一方面指編碼范圍的局限性:比如ASCII只能表示128個(gè)字符抬闷。還有編碼兼容性方面的局限性:比如ISO8859代表的一系列編碼字符集雖然可以表示大部分國(guó)家地區(qū)的字符妇蛀,但是彼此的兼容性做的不好。Unicode的目標(biāo)就如同其名稱的含義一樣:“實(shí)現(xiàn)字符編碼統(tǒng)一”
Unicode標(biāo)準(zhǔn)的實(shí)現(xiàn)方案有如下三種:UTF-8笤成、UTF-16和UTF-32**.
UTF-8是變長(zhǎng)編碼讥耗,使用1到4個(gè)字節(jié)。UTF-8在設(shè)計(jì)時(shí)考慮到向前兼容疹启,所以其前128個(gè)字符和ASCII完全一樣,也就是說(shuō)蔼卡,所有ASCII同時(shí)也都符合UTF-8編碼格式喊崖。其格式如下:
0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
字節(jié)首部為0的話,也就是前面說(shuō)的ASCII了雇逞。此外荤懂,字節(jié)首部連續(xù)1的個(gè)數(shù)就代表了該字符編碼后所占的字節(jié)數(shù)。目前全世界的網(wǎng)頁(yè)編碼絕大多數(shù)使用的就是UTF-8塘砸,占比接近90%节仿。
UTF-16也是變長(zhǎng)編碼,但其最初是固定16-bit寬度的定長(zhǎng)編碼掉蔬,主要因?yàn)閁nicode涵蓋的字符太多了廊宪。兩字節(jié)更本不夠用!
UTF-32是32-bit定長(zhǎng)編碼女轿,優(yōu)點(diǎn):定長(zhǎng)編碼在處理效率上相對(duì)于變長(zhǎng)編碼要高箭启,此外,可通過(guò)索引訪問(wèn)任意字符是其另一大優(yōu)勢(shì)蛉迹;缺點(diǎn)也很明顯:32bit太浪費(fèi)了傅寡!存儲(chǔ)效率太低!
big-endian和little-endian?在多字節(jié)編碼標(biāo)準(zhǔn)中可能會(huì)遇到這樣的問(wèn)題:假如一個(gè)字符用兩個(gè)字節(jié)表示荐操,那么當(dāng)讀取這個(gè)字符的時(shí)候芜抒,哪個(gè)字節(jié)表示高有效位?哪個(gè)表示低有效位呢托启?這就涉及到字節(jié)的存儲(chǔ)順序問(wèn)題宅倒。在Unicode中UTF-16和UTF-32都會(huì)面臨這個(gè)問(wèn)題。通常用BOM(Byte Order Mark)來(lái)進(jìn)行區(qū)分驾中。BOM用一個(gè)"U+FEFF"來(lái)表示唉堪,這個(gè)值在
Unicode中是沒(méi)有對(duì)應(yīng)字符的。不僅可以用其來(lái)指定字節(jié)順序肩民,還可以表示字節(jié)流的編碼方式唠亚。
System.out.println("len1:" + "a".getBytes("UTF16").length);
System.out.println("len2:" + "aa".getBytes("UTF16").length);
輸出結(jié)果:
len1:4
len2:6
為什么是4和6,不應(yīng)該是2和4嗎3痔怠灶搜?。輸出編碼后的字節(jié)序列可以發(fā)現(xiàn)工窍,起始的兩個(gè)字節(jié)都是:"fe ff"割卖。
Java的char類型用什么編碼格式?Java語(yǔ)言規(guī)范規(guī)定了Java的char類型使用的是UTF-16患雏。這就是為什么Java的char占用兩個(gè)字節(jié)的原因鹏溯。此外,Java標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)的對(duì)char與String的序列化規(guī)定使用UTF-8淹仑。Java的Class文件中的字符串常量與符號(hào)名字也都規(guī)定用UTF-8編碼丙挽。這大概是當(dāng)時(shí)設(shè)計(jì)者為了平衡運(yùn)行時(shí)的時(shí)間效率(采用定長(zhǎng)編碼的UTF-16,當(dāng)然匀借,在設(shè)計(jì)java的時(shí)候UTF-16還是定長(zhǎng)的)與外部存儲(chǔ)的空間效率(采用變長(zhǎng)的UTF-8編碼)而做的取舍颜阐。
1.4 GBK
GBK是用于對(duì)簡(jiǎn)體中文進(jìn)行編碼。每個(gè)字符用兩字節(jié)表示吓肋,同時(shí)兼容GB2312標(biāo)準(zhǔn)凳怨。
2. 可能發(fā)生的中文亂碼
這一小節(jié)介紹軟件開發(fā)中常見的中文編碼亂碼問(wèn)題,在下面示例中:對(duì)于給定的一個(gè)包含中文的字符串"你好Java"是鬼,看一下都會(huì)出現(xiàn)哪些亂碼問(wèn)題肤舞。
2.1 中文變問(wèn)號(hào),如:?????
"你好Java" ------> "??Java"
這種情況一般是由于中文字符經(jīng)ISO8859-1編碼造成的均蜜。下面是編碼的具體過(guò)程:
原字符串:"你好Java"
你 | 好 | J | a | v | a |
---|---|---|---|---|---|
4f60 | 597d | 4a | 61 | 76 | 61 |
經(jīng)ISO8859-1編碼后:
你 | 好 | J | a | v | a |
---|---|---|---|---|---|
3f | 3f | 4a | 61 | 76 | 61 |
編碼后字符串:"??Java"
String str = "你好Java";
System.out.println(byteToHexString(str.getBytes(CHARSET_ISO88591)));
System.out.println(new String(str.getBytes(CHARSET_ISO88591)));
輸出:
3f 3f 4a 61 76 61
??Java
我們知道ISO8859-1是單字節(jié)編碼萨赁,而對(duì)于漢字已經(jīng)超出ISO8859-1的編碼范圍,會(huì)被轉(zhuǎn)化為"3f"兆龙,我們查表可知杖爽,"3f"對(duì)應(yīng)的字符正是"?"敲董。
中文變問(wèn)號(hào)的亂碼情況是非常常見的,大部分開源軟件的默認(rèn)編碼設(shè)置成了ISO8859-1慰安,這點(diǎn)需要格外注意腋寨。
2.2 中文變奇怪字符,如:?? ?¥? 或者 ??o?
"你好Java" ------> "?? ?¥?Java"
原字符串:"你好Java"
你 | 好 | J | a | v | a |
---|---|---|---|---|---|
4f60 | 597d | 4a | 61 | 76 | 61 |
經(jīng)UTF-8編碼后化焕,一個(gè)中文用三個(gè)字節(jié)表示:
你 | 好 | J| a| v| a
---|---|---|---|---|---|---|---
e4 bd a0 | e5 a5 bd | 4a| 61| 76| 61
亂碼原因:UTF8編碼或GBK編碼萄窜,再由ISO8859-1解碼。對(duì)照ISO8859-1編碼表后發(fā)現(xiàn):e4 bd a0分別對(duì)應(yīng)三個(gè)字符:"?? ",e5 a5 bd分別對(duì)應(yīng)三個(gè)字符"?¥?",
2.3 中文變“復(fù)雜中文”如:浣犲ソ
下面依然是"你好Java"經(jīng)過(guò)UTF-8編碼后對(duì)應(yīng)的字節(jié)序列:
你 | 好 | J| a| v| a
---|---|---|---|---|---|---|---
e4 bd a0 | e5 a5 bd | 4a| 61| 76| 61
在GBK表中查找:e4 bd對(duì)應(yīng)字符:"浣",a0 e5對(duì)應(yīng)字符:"犲",a5 bd對(duì)應(yīng)字符:"ソ"
同理撒桨,如果GBK編碼的中文用UTF-8來(lái)解碼的話查刻,同樣會(huì)出現(xiàn)亂碼問(wèn)題。
2.4 中文變成一堆黑色菱形+問(wèn)號(hào)凤类,如:?????
首先問(wèn)號(hào)+黑色菱形的字符是Unicode中的"REPLACEMENT CHARACTER",該字符的主要作用是用來(lái)表示不識(shí)別的字符穗泵。
所以產(chǎn)生亂碼的原因可能有很多,下面通過(guò)原字符串:"你好Java"谜疤,重現(xiàn)一種亂碼方式:
原字符串:String str = "你好Java"
你 | 好 | J| a| v| a
---|---|---|---|---|---
4f60 | 597d | 4a| 61| 76| 61
UTF-16編碼后
fe ff 4f 60 59 7d 0 4a 0 61 0 76 0 61
其中"fe ff"就是字節(jié)流起始的BOM標(biāo)識(shí)符佃延。"fe ff"在Unicode標(biāo)準(zhǔn)中屬于"noncharacters",只用于內(nèi)部使用。所以夷磕,
在輸出該字節(jié)序列的時(shí)候履肃,沒(méi)有該碼元對(duì)應(yīng)的字符,對(duì)于不識(shí)別字符坐桩,就會(huì)用??替代尺棋。
3. Web開發(fā)中涉及到的中文編解碼
Web中的數(shù)據(jù)大多通過(guò)http協(xié)議進(jìn)行傳輸,所涉及到的一些編解碼問(wèn)題都圍繞著http協(xié)議绵跷。下面以Tomcat作為Web服務(wù)器膘螟,
探討下一個(gè)完整的請(qǐng)求響應(yīng)流程中哪些地方會(huì)涉及到中文的編解碼。
3.1 url編解碼
web環(huán)境中的中文亂碼問(wèn)題抖坪,實(shí)驗(yàn)如下:
jsp中的form表單:
<body>
<form name="form" method="post" action="manager/codec/你好">
<table>
<tr>
<td>用戶名: <input type="text" name="name" id="name" />
</td>
<td>地址 <input type="text" name="address" id="address" />
</td>
<th><input type="submit" name="submit" value="保存" /></th>
</tr>
</table>
</form>
</body>
后端使用SpringMVC的Controller:
@Controller()
@RequestMapping("/manager")
public class ManagerController {
@RequestMapping("/test/{param}")
@ResponseBody
public String test(@PathVariable String param, HttpServletRequest request){
String name = request.getParameter("name");
System.out.println("name:" + name + ",param:" + param);
return "test";
}
}
表單中填入內(nèi)容:
用戶名:你好 Java
地址:123
提交請(qǐng)求,firebug中的顯示的url如下:
http://localhost:8080/fdyuntu-ssm/manager/codec/%E4%BD%A0%E5%A5%BD
查閱編碼可以闷叉,firefox對(duì)url中出現(xiàn)的中文使用了UTF-8的編碼方式擦俐。之所以u(píng)rl中出現(xiàn)%,這是因?yàn)楦鶕?jù)URL編碼規(guī)范握侧,瀏覽器會(huì)將非ASCII字符編成16進(jìn)制后蚯瞧,每個(gè)字節(jié)前需要加%。
后端控制臺(tái)輸出:
name:?? ?¥? Java,param:?? ?¥?
可見無(wú)論是url中的中文信息或是post表單中的中文都出現(xiàn)了亂碼現(xiàn)象品擎,從前一節(jié)中關(guān)于亂碼情況的分析來(lái)看埋合,這里應(yīng)該是中文字符經(jīng)過(guò)瀏覽器UTF-8編碼后,Server端用ISO8859-1進(jìn)行解碼所致萄传。下面逐個(gè)分析url和post表單如何進(jìn)行編解碼的甚颂。
在tomcat中url的byte -> char的轉(zhuǎn)換是在org.apache.catalina.connector.CoyoteAdapter類的convertURI(MessageBytes uri, Request request)方法中執(zhí)行的,源碼如下:
protected void convertURI(MessageBytes uri, Request request)throws Exception {
ByteChunk bc = uri.getByteChunk();
int length = bc.getLength();
CharChunk cc = uri.getCharChunk();
cc.allocate(length, -1);
//這里獲取的connector的URIEncoding屬性,即server.xml文件中connector元素的URIEncoding屬性
String enc = connector.getURIEncoding();
if (enc != null) {
B2CConverter conv = request.getURIConverter();
try {
if (conv == null) {
conv = new B2CConverter(enc, true);
request.setURIConverter(conv);
} else {
conv.recycle();
}
} catch (IOException e) {
log.error("Invalid URI encoding; using HTTP default");
connector.setURIEncoding(null);
}
if (conv != null) {
try {
conv.convert(bc, cc, true);
uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());
return;
} catch (IOException ioe) {
request.getResponse().sendError(
HttpServletResponse.SC_BAD_REQUEST);
}
}
}
// 如果沒(méi)有配置URIEncoding振诬,則在ByteChunk中默認(rèn)使用ISO8859-1蹭睡。
byte[] bbuf = bc.getBuffer();
char[] cbuf = cc.getBuffer();
int start = bc.getStart();
for (int i = 0; i < length; i++) {
cbuf[i] = (char) (bbuf[i + start] & 0xff);
}
uri.setChars(cbuf, 0, length);
}
在org.apache.tomcat.util.buf.ByteChunk中可以看到默認(rèn)編碼的定義:
public final class ByteChunk implements Cloneable, Serializable {
//。赶么。肩豁。
public static final Charset DEFAULT_CHARSET = B2CConverter.ISO_8859_1;
//。辫呻。清钥。
}
所以對(duì)于請(qǐng)求url中的中文,我們按UTF-8進(jìn)行編碼放闺,在服務(wù)端卻按ISO8859-1進(jìn)行解碼祟昭,所以出現(xiàn)亂碼現(xiàn)象。我們可以再Tomcat的server.xml中指定url的編解碼格式雄人,如下:
<Connector URIEncoding="UTF-8" 从橘。。础钠。>
此時(shí)重復(fù)上面實(shí)驗(yàn)恰力,后端控制臺(tái)輸出:name:?? ?¥? Java,param:你好
雖然url中的參數(shù)可以正常顯示了,但是form表單中的參數(shù)name依然亂碼,下面進(jìn)一步分析庆猫。
3.2 form表單元素的編解碼
name參數(shù)的編碼依然是亂碼的凸丸,為啥?首先定位form表單中參數(shù)是在哪里進(jìn)行解碼的香府。Form表單中的字符解碼時(shí)機(jī)是發(fā)生在第一次調(diào)用request.getParameter時(shí),可以通過(guò)request.setCharacterEncoding設(shè)置码倦。需要注意的是setCharacterEncoding必須在getParameter之前調(diào)用企孩!否則,setCharacterEncoding不會(huì)起作用袁稽。
Tomcat中HttpServletRequest接口的實(shí)現(xiàn)類是org.apache.catalina.connector.Request勿璃。下面是Request類中g(shù)etParameter源碼:
@Override
public String getParameter(String name) {
//判斷參數(shù)是否被解析過(guò)
if (!parametersParsed) {
parseParameters();//第一次參數(shù)解析
}
return coyoteRequest.getParameters().getParameter(name);
}
//下面是parseParameters部分源碼
protected void parseParameters() {
//設(shè)為true,表示參數(shù)已解析過(guò)
parametersParsed = true;
//Parameters對(duì)象封裝了form表單參數(shù)
Parameters parameters = coyoteRequest.getParameters();
boolean success = false;
try {
// Set this every time in case limit has been changed via JMX
parameters.setLimit(getConnector().getMaxParameterCount());
//獲取字符編碼格式
String enc = getCharacterEncoding();
boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
if (enc != null) {
//getCharacterEncoding不為null推汽,則對(duì)應(yīng)設(shè)置編碼方式
parameters.setEncoding(enc);
if (useBodyEncodingForURI) {
parameters.setQueryStringEncoding(enc);
}
} else {
//如果enc為null补疑,則編碼方式設(shè)置為DEFAULT_CHARACTER_ENCODING,也就是ISO8859-1
parameters.setEncoding
(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
if (useBodyEncodingForURI) {
parameters.setQueryStringEncoding
(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
}
}
parameters.handleQueryParameters();
歹撒。莲组。。
}
}
從以上源碼中可以看出為什么需要在第一次調(diào)用getParameter之前設(shè)置CharacterEncoding暖夭。因?yàn)榈谝淮螆?zhí)行parseParameters時(shí)锹杈,會(huì)把parametersParsed變量設(shè)為true撵孤。所以parseParameters只會(huì)在第一次getParameter時(shí)調(diào)用。有時(shí)會(huì)出現(xiàn)這么一種怪像:通過(guò)request.getCharacterEncoding()得到的是我們認(rèn)為正確的編碼字符集嬉橙,但是request.getParameter得到的依然是亂碼早直。此時(shí)就需要考慮下我們調(diào)用setCharacterEncoding之前是否已經(jīng)調(diào)用過(guò)getParameter方法了。
經(jīng)過(guò)上面的分析后市框,對(duì)于form表單參數(shù)亂碼問(wèn)題就很好解決了霞扬,在第一次調(diào)用request.getParameter方法前,通過(guò)request.setCharacterEncoding("Expected_Encoding");設(shè)置即可枫振。這一步可以用Servlet標(biāo)準(zhǔn)中的Filter實(shí)現(xiàn)喻圃,不過(guò),常用的MVC框架中已經(jīng)有現(xiàn)成的Filter實(shí)現(xiàn)了粪滤,比如SpringMVC中的org.springframework.web.filter.CharacterEncodingFilter,如下:
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
request.setCharacterEncoding(this.encoding);//設(shè)置指定的編碼
if (this.forceEncoding) {
response.setCharacterEncoding(this.encoding);
}
}
filterChain.doFilter(request, response);
}
3.3 JSP中涉及的編碼
jsp中可以通過(guò)page指令指定一些編碼參數(shù)斧拍,如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
pageEncoding="UTF-8"在什么時(shí)候起作用?
在Servlet標(biāo)準(zhǔn)中杖小,jsp最終也會(huì)被編譯成一個(gè)servlet肆汹。index.jsp->index_jsp.java.pageEncoding="UTF-8"就是在這個(gè)解析過(guò)程中起作用的。
contentType="text/html; charset=UTF-8"的作用予权?
contentType是響應(yīng)頭中特定信息昂勉,主要的作用是告訴瀏覽器response中存放的主體對(duì)象類型和編碼,這樣瀏覽器就可以對(duì)指定類型進(jìn)行正確解碼扫腺,保證了數(shù)據(jù)在server和client端的一致性岗照。當(dāng)進(jìn)行Servlet編程的時(shí)候,可以手動(dòng)進(jìn)行設(shè)置笆环,如下:
response.setContentType("text/html; charset=UTF-8");
3.4 文件的上傳和下載中涉及到的中文亂碼
Web中的文件操作主要是上傳和下載攒至,這個(gè)過(guò)程也是依托于Http協(xié)議作為數(shù)據(jù)載體。所以躁劣,最終是否亂碼重點(diǎn)在于是否正確的設(shè)置http的request迫吐、response的header中的相關(guān)字段。如ContentType账忘、Content-Disposition的設(shè)定等志膀。如下:
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("application/x-msdownload");
response.addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
這里需要注意的是Content-Disposition的filename屬性值,如果fileName含有中文闪萄,那么要格外注意fileName字符串的編碼格式梧却。在rfc5987對(duì)于HTTP的Header中參數(shù)的編碼做出了明確的規(guī)定:
By default, message header field parameters in Hypertext Transfer Protocol (HTTP) messages cannot carry characters outside the ISO-8859-1 character set.
也就是說(shuō)默認(rèn)情況下奇颠,Http的Header中的參數(shù)只能用ISO-8859-1字符集中的字符败去,那么是否意味著Content-Disposition中的fileName字符串也要轉(zhuǎn)成ISO-8859-1了呢?答案是:NO烈拒!原因如下:Content-Disposition其實(shí)不屬于Http/1.1標(biāo)準(zhǔn)圆裕。這在RFC2616中有明確的說(shuō)明广鳍。只因?yàn)槠涫褂脧V泛,HTTP才對(duì)其支持吓妆。在rfc6266中也詳細(xì)介紹了Content-Disposition的filename參數(shù)含義和用法赊时。下面是對(duì)于下載包含中文名稱的文件時(shí)的解決方案。
解決方案
最簡(jiǎn)單就是直接用ISO8859-1對(duì)文件名進(jìn)行編碼行拢,大多數(shù)瀏覽器都支持祖秒。如下:
exportFileName.getBytes("UTF-8"),"ISO8859-1");//這里的UTF-8也可能是別的編碼,主要依據(jù)系統(tǒng)默認(rèn)的編碼來(lái)設(shè)定舟奠。
或通過(guò)其它編碼竭缝,如UTF-8。
response.addHeader("Content-Disposition",
"attachment; filename*=UTF-8''" + URLEncoder.encode(exportFileName, "UTF8"));
4. 總結(jié)
編解碼問(wèn)題是多語(yǔ)言交互系統(tǒng)中必然要面對(duì)的問(wèn)題沼瘫,尤其對(duì)于中文環(huán)境中的開發(fā)者來(lái)說(shuō)抬纸,在入門階段或多或少都會(huì)遇到此類問(wèn)題。亂碼問(wèn)題本質(zhì)就是通信雙方使用的標(biāo)準(zhǔn)不一致耿戚。所以湿故,解決亂碼問(wèn)題的方法其實(shí)也很簡(jiǎn)單,統(tǒng)一下編解碼標(biāo)準(zhǔn)即可膜蛔。此外坛猪,深入理解各種編碼標(biāo)準(zhǔn)的原理和關(guān)系也非常重要,在以后遇到類似問(wèn)題的時(shí)候能夠更加準(zhǔn)確的判斷出造成亂碼的原因飞几。