在開發的過程中,我們不可避免的會遇到各種各樣的編碼,解碼,或者亂碼問題,很多時候,我們可以正常的解決問題,但是說實在的,我們有可能并不清楚問題到底是怎么被解決的,秉承知其然,更要知其所以然的理念,經過一番研究,就有了下面的這篇文章,鑒于本人功力尚淺,有錯誤請給予糾正 :-)
編碼解碼核心
簡單的來說,編碼是從一個字符,比如‘郭',到一段二進制碼流的過程。解碼是從一段二進制碼流到一個字符的過程。
但是,就計算機工作原理而言,這其中涉及到了三個對象。
•字符 (我們在各種終端上面看得到的顯示結果)
•內碼 (對應顯示的字符的計算機存儲數據)
•字符集 (內碼在內存中的具體實現)
這三者之間的配合如下圖。
字符
對于字符而言,是我們程序員而言想必是最熟悉的了吧。什么Abs_=+/.80,都是我們所熟悉使用的字符。雖然我們表面上看到的是一個個的字符,但是在計算機而言,其真正識別和處理的不過是對應于顯示的字符的一個個的內碼。
內碼
內碼是漢字在計算機內部存儲,處理和傳輸用的信息編碼。它必須與ASCII碼兼容但又不能沖突。
也許你會想,ASCII碼又是什么? 對此,百度百科是這樣解釋的:
ASCII(American Standard Code for Information Interchange,美國信息交換標準代碼)是基于拉丁字母的一套電腦編碼系統,主要用于顯示現代英語和其他西歐語言。它是現今最通用的單字節編碼系統,并等同于國際標準ISO/IEC 646。
從這里我們不禁會想,既然是單字節編碼,那么漢字這種多字節表示的信息又是怎么被計算機識別和處理的呢?
國標碼規定:一個漢字用兩個字節來表示,每個字節只用前七位,最高位均未作定義。但我們要注意,國標碼不同于ASCII碼,并非漢字在計算機內的真正表示代碼,它僅僅是一種編碼方案,計算機內部漢字的代碼叫做漢字機內碼,簡稱漢字內碼。
所以,這也是國人在平時開發過程中經常會遇到的 亂碼問題的根源。
字符集
字符集作為內碼在內存中的具體實現,肩負著很大的責任。
•ascii不僅僅指英文對應的內碼,還包括它的具體實現,也就是它的字符集。它是用一個字節存儲每個內碼的。
•unicode是所有文字(包括英文,中文,日文等)所對應的內碼的集合。
unicode的實現方式比較多樣,常用的有UTF-8,GBK,GB18030。
•其中,UTF-8是一種不定長的內碼實現方式。
GB18030兼容GBK,GBK兼容GB2312。
嚴格點來講,我們所謂的編碼解碼問題:編碼指內碼編碼成字符集;解碼指字符集解碼為內碼
-----------------------------------------------
系統編碼
眾所周知,在不同的操作系統上,支持的編碼也是不太一致的。所以我們在跨平臺操作的時候,需要重點考慮的問題就包括系統編碼問題。
windows
在Windows上查詢當前系統的活動代碼頁,就可以知道當前系統使用的編碼。
調出命令行之后輸入: chcp 即可。
基本上國人的電腦上會顯示936
936 代表GBK 擴展的EUC-CN 編碼( GB 2312-80編碼,包含 6763 個漢字)到Unicode (GB13000.1-93)中定義的20902個漢字,即中國大陸使用的是簡體中文zh_CN.。
Linux
在Linux操作系統上查看系統編碼更是方便。
locale是最核心的一個變量。它包括12個基本屬性。這12個基本屬性構成某個地區的語言習慣,日期,貨幣,單位等文化因素。LC_ALL是強制修改locale信息的命令。LANG是locale的默認設置命令。因此,當LC_ALL強制locale信息以后,LANG的設置也就失效了。
當然了,我們可以根據自己的喜好,來為自己的linux計算機分配系統編碼。
----------------------------------------------------
Python中的編碼問題
一般而言,談到程序編碼問題,首屈一指的就是Python語言中的編碼了。尤其是Unicode字符集的使用,更是讓人摸不著頭腦。不過,現在不用擔心了,待會我會出絕招滴。
系統編碼
經過了剛才的知識的鋪墊,大家肯定對此也有了一定的感悟了。Python中的系統編碼充當了一個橋梁的作用。其通常也是寫源碼的編輯器的編碼方式。它代表源碼文件內的所有內容都是根據詞方式編碼成二進制碼流,存入到磁盤中的。
系統編碼可以通過locale命令查看(LINUX)。
說白咯,就是操作系統是怎么存儲我們的Python源程序的。
Python編碼
這是一個比較新的概念,也是Python編碼問題的根源。那就是指python內設置的解碼方式。如果不設定的話,python默認是ascii解碼。所以我們不難理解為什么有漢字的時候很容易出現亂碼的情況了吧。
為了解決Python默認編碼不支持漢字的窘境。我們需要手動的設置,讓其識別咱們可愛的博大精深的漢字。一般有如下三種方式。
方式一
只能Python識別
在源碼文件開頭(一定要是第一行):#coding=UTF-8,源碼文件的設置解碼方式為UTF-8
方式二
可以被其他的語言識別
在源碼文件開頭(一定是第一行):#--coding:UTF-8--,源碼文件的設置解碼方式是UTF-8
方式三
我經常使用的方式是方式一加方式三
1
2
3
|
import sys reload(sys) sys.setdefaultencoding('UTF-8') |
文件編碼
對于文件編碼,大體意思就是系統為我們即將存儲的文件而進行的編碼操作。
實例一
1
2
3
4
5
|
系統編碼:locale:gbk python源文件test.py #coding='UTF-8' s='郭' print s |
在test.py被保存的那一刻,系統會以gbk的方式將數據存儲到本地硬盤上,而在下次我們運行這段代碼的時候,Python編碼會按照源文件開頭指定的utf-8編碼來解碼并運行,所以遇到'郭'的時候,由于碼制的不同,出現亂碼或者出現錯誤就不難理解了。
實例二
1
2
3
4
5
|
系統編碼:locale:gbk test.py #coding='gbk' s='郭' ss=s.encode('UTF-8') |
這個時候,系統編碼仍然會以gbk的變啊買房時對源文件進行編碼處理,但是在遇到ss=s.encode('UTF-8')的時候,會先以Python編碼指定的方式將對應的字符串(二進制碼流)編碼,再按照系統編碼對其進一步的編碼處理。
而在下次加載的時候,程序會被以對二進制碼流,按照解碼的編碼處理原則處理。也就是說Python解釋到ss=s.encode('UTF-8')行的時候,會對相應的二進制碼流以utf-8的方式解碼,這樣就能很好的解決掉字符集不匹配的問題了。
二進制碼流(python中,所有字符串都表示的是相應的二進制碼流,所有的unicode都表示的是相應的內碼)
Python中字符串和Unicode的區別
字符串表示的是編碼后的二進制碼流,unicode表示的是內碼。所以,為了避免解碼錯誤的出現,最好使用unicode表示
unicode的定義,使用
•s=u'郭':定義unicode字符串s。s表示的是哈的unicode內碼
•ss=unicode(s,'gbk'):對字符串s按照gbk方式解碼,ss表示解碼后的內碼
•import codecs
f=codecs.open(filename,'r','gbk‘)
s=f.read()
按照gbk方式讀取filename,讀取后的內容轉變成unicode內碼存在變量s中。
了解了以上編碼解碼的底層原理之后,基本上就可以應對相關的亂碼問題了。
-----------------------------------------------------
PHP中的編碼
在PHP中我們也經常會遇到頁面出現亂碼的情況,有了上面的知識基礎,我們再過來看PHP中的亂碼問題,就會迎刃而解了。當然了,今天主要是來講一講如何解決PHP中文亂碼問題。
header
1
|
header("Content-Type:text/html;charset=utf-8"); |
加上頭信息的作用就是告訴瀏覽器,以XX方式來解碼。基本上而言不會有太大的問題,但是前提是雙方的系統編碼能保持一致,否則也會出現由于系統編碼不同而引起的亂碼問題,對照Python的案例,我們也很容易可以理解。
set names XX
1
|
mysql_query("set names UTF-8"); |
這個是對數據庫數據亂碼的很好的解決辦法。說白了這就是所謂的“系統編碼不同而引起的亂碼問題的一類”。就拿MySQL數據庫而言,我們是以客戶端的形式來訪問的數據,如果MySQL客戶端的編碼為gbk,而我們的瀏覽器解碼使用的是UTF-8,請問怎么可能不會亂碼呢?
只要保持雙方的編碼,解碼方式一致,亂碼問題,不攻自破。
-------------------------------------------------------
數據庫中的編碼
通過剛才的學習,類比系統編碼和文件編碼。我們經常遇到的數據庫信息存取出現亂碼問題就可以很容易的解決了。
修改字符集設置即可。但是這不是一個好習慣,比較好的做法是修改讀取出來的數據的編碼。
以MySQL為例。
可以先輸入查詢語句SHOW VARIABLES LIKE ‘character_set_%';,查看所有的編碼是否是UTF-8.
如果不是可以使用Server Instance Config 把默認的字符集設置為utf-8或者修改/MySQL/MySQL Server 5.0/my.ini中的default-character-set=gbk
1
|
character-set-server=gbk; |
然后重新啟動mysql的服務就行了
JSP亂碼
在開發JavaWeb的時候,遇到JSP亂碼的情況有很多,下面著重的來探討一下解決方案呢。
JSP頁面本身
每個頁面上加上 這樣在jsp頁面里,點右鍵,查看編碼方式則為UTF-8.
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
也可以把設置myEclipse中默認的contentTyepe。步驟是:windows->preferences->Generl->ContentType.然后設置成UTF-8.一定要update~~
在JSP頁面頭部加入下面這句話,告訴瀏覽器應該調用UTF-8的字符集。
或者使用HTML標簽來聲明
1
|
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" /> |
數據庫連接語句
•設置characterencoding為UTF-8 如jdbc.mysql.url=jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF8
•如果使用Hibernate,那就把所有的配置文件頭部的編碼格式改成UTF-8。
Tomcat方面
為了保證get/post數據都采用相同的UTF8編碼,我們在server.xml中進行了如下設置:
1
2
|
< Connector port = "8080" protocol = "HTTP/1.1" connectionTimeout = "20000" redirectPort = "8443" URIEncoding = "UTF-8" /> |
過濾器Filter
有些時候,使用過濾器可以一勞永逸的解決亂碼問題,原理就是強化了通信雙方的編碼一致性。
1
2
3
4
5
6
7
8
9
10
|
//在doFilter方法中添加這樣的代碼 HttpServletRequest request = (HttpServletRequest )req; HttpServletResponse response = (HttpServletResponse )resp; request.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type:text/htmlcharset=UTF-8"); response.setCharacterEncoding("UTF-8"); //放行 chain.doFilter(request,response); |
總結
回顧一下,解決亂碼問題的核心就是保證編碼與解碼工作的一致性。無論是系統編碼還是文件編碼,保證編碼工作和解碼工作的反向一致性,基本上就不會出現亂碼問題。
如果你發現文章中有不恰當的地方,歡迎批評指正。
以上這篇淺談編碼,解碼,亂碼的問題就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。