溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java字符編碼知識(shí)簡(jiǎn)介(zt)

發(fā)布時(shí)間:2020-08-08 11:07:13 來源:ITPUB博客 閱讀:229 作者:tolywang 欄目:編程語言

新一篇: orcle ibatis 中文字符 問題
1 基本信息
摘要:Java應(yīng)用程序特別是Web應(yīng)用中,經(jīng)常遇到字符的編碼問題。為了防止出現(xiàn)亂碼,首先需要了解字符編碼的基本概念以及Java是如何處理字符編碼的,這樣就可以有目的地在輸入/輸出環(huán)節(jié)中增加必要的轉(zhuǎn)碼。本文將分以下幾部分介紹:
1. 什么是字符集?什么是編碼?
2. 常用字符集有哪些?
3. 為什么會(huì)有亂碼?
4. Java字符編碼
5. JSP編碼
6. 有沒有萬金油?
7. 參考資料和推薦參閱

[@more@]

作者:賈繼東 創(chuàng)建于2007-9-17

2 什么是字符集?什么是編碼?
l 字符(Character)是文字與符號(hào)的總稱,包括文字、圖形符號(hào)、數(shù)學(xué)符號(hào)等。
l 字符集(Charset)就是一組抽象字符的集合。
字符集常常和一種具體的語言文字對(duì)應(yīng)起來,該文字中的所有字符或者大部分常用字符就構(gòu)成了該文字的字符集,比如英文字符集。
一組有共同特征的字符也可以組成字符集,比如繁體漢字字符集、日文漢字字符集。
字符集的子集也是字符集。
計(jì)算機(jī)要處理各種字符,就需要將字符和二進(jìn)制內(nèi)碼對(duì)應(yīng)起來,這種對(duì)應(yīng)關(guān)系就是字符
l 編碼(Encoding)
制定編碼首先要確定字符集,并將字符集內(nèi)的字符排序,然后和二進(jìn)制數(shù)字對(duì)應(yīng)起來。根據(jù)字符集內(nèi)字符的多少,會(huì)確定用幾個(gè)字節(jié)來編碼。
每種編碼都限定了一個(gè)明確的字符集合,叫做被編碼過的字符集(Coded Character Set),這是字符集的另外一個(gè)含義。通常所說的字符集大多是這個(gè)含義。
3 常用字符集有哪些?
ASCII
American Standard Code for Information Interchange,美國(guó)信息交換標(biāo)準(zhǔn)碼。
目前計(jì)算機(jī)中用得最廣泛的字符集及其編碼,由美國(guó)國(guó)家標(biāo)準(zhǔn)局(ANSI)制定。 它已被國(guó)際標(biāo)準(zhǔn)化組織(ISO)定為國(guó)際標(biāo)準(zhǔn),稱為ISO 646標(biāo)準(zhǔn)。 ASCII字符集由控制字符和圖形字符組成。 在計(jì)算機(jī)的存儲(chǔ)單元中,一個(gè)ASCII碼值占一個(gè)字節(jié)(8個(gè)二進(jìn)制位),其最高位(b7)用作奇偶校驗(yàn)位。所謂奇偶校驗(yàn),是指在代碼傳送過程中用來檢驗(yàn)是否出現(xiàn)錯(cuò)誤的一種方法,一般分奇校驗(yàn)和偶校驗(yàn)兩種。奇校驗(yàn)規(guī)定:正確的代碼一個(gè)字節(jié)中1的個(gè)數(shù)必須是奇數(shù),若非奇數(shù),則在最高位b7添1。偶校驗(yàn)規(guī)定:正確的代碼一個(gè)字節(jié)中1的個(gè)數(shù)必須是偶數(shù),若非偶數(shù),則在最高位b7添1。
ISO 8859-1
全稱ISO/IEC 8859,是國(guó)際標(biāo)準(zhǔn)化組織(ISO)及國(guó)際電工委員會(huì)(IEC)聯(lián)合制定的一系列8位字符集的標(biāo)準(zhǔn),現(xiàn)時(shí)定義了15個(gè)字符集。
ASCII收錄了空格及94個(gè)“可印刷字符”,足以給英語使用。但是,其他使用拉丁字母的語言(主要是歐洲國(guó)家的語言),都有一定數(shù)量的變音字母,故可以使用ASCII及控制字符以外的區(qū)域來儲(chǔ)存及表示。除了使用拉丁字母的語言外,使用西里爾字母的東歐語言、希臘語、泰語、現(xiàn)代阿拉伯語、希伯來語等,都可以使用這個(gè)形式來儲(chǔ)存及表示。
² ISO 8859-1 (Latin-1) - 西歐語言
² ISO 8859-2 (Latin-2) - 中歐語言
² ISO 8859-3 (Latin-3) - 南歐語言。世界語也可用此字符顯示。
² ISO 8859-4 (Latin-4) - 北歐語言
² ISO 8859-5 (Cyrillic) - 斯拉夫語言
² ISO 8859-6 (Arabic) - 阿拉伯語
² ISO 8859-7 (Greek) - 希臘語
² ISO 8859-8 (Hebrew) - 希伯來語(視覺順序)
² ISO 8859-8-I - 希伯來語(邏輯順序)
² ISO 8859-9 (Latin-5 或 Turkish) - 它把Latin-1的冰島語字母換走,加入土耳其語字母。
² ISO 8859-10 (Latin-6 或 Nordic) - 北日耳曼語支,用來代替Latin-4。
² ISO 8859-11 (Thai) - 泰語,從泰國(guó)的 TIS620 標(biāo)準(zhǔn)字集演化而來。
² ISO 8859-13 (Latin-7 或 Baltic Rim) - 波羅的語族
² ISO 8859-14 (Latin-8 或 Celtic) - 凱爾特語族
² ISO 8859-15 (Latin-9) - 西歐語言,加入Latin-1欠缺的法語及芬蘭語重音字母,以及歐元符號(hào)。
² ISO 8859-16 (Latin-10) - 東南歐語言。主要供羅馬尼亞語使用,并加入歐元符號(hào)。
很明顯,iso8859-1編碼表示的字符范圍很窄,無法表示中文字符。 但是,由于是單字節(jié)編碼,和計(jì)算機(jī)最基礎(chǔ)的表示單位一致,所以很多時(shí)候,仍舊使用iso8859-1編碼來表示。 而且在很多協(xié)議上,默認(rèn)使用該編碼。
UCS
通用字符集(Universal Character Set,UCS)是由ISO制定的ISO 10646(或稱ISO/IEC 10646)標(biāo)準(zhǔn)所定義的字符編碼方式,采用4字節(jié)編碼。
UCS包含了已知語言的所有字符。除了拉丁語、希臘語、斯拉夫語、希伯來語、阿拉伯語、亞美尼亞語、格魯吉亞語,還包括中文、日文、韓文這樣的象形文字,UCS還包括大量的圖形、印刷、數(shù)學(xué)、科學(xué)符號(hào)。
² UCS-2: 與unicode的2byte編碼基本一樣。
² UCS-4: 4byte編碼, 目前是在UCS-2前加上2個(gè)全零的byte。
Unicode
Unicode(統(tǒng)一碼、萬國(guó)碼、單一碼)是一種在計(jì)算機(jī)上使用的字符編碼。
Unicodehttp//www.unicode.org制定的編碼機(jī)制, 要將全世界常用文字都函括進(jìn)去。它為每種語言中的每個(gè)字符設(shè)定了統(tǒng)一并且唯一的二進(jìn)制編碼,以滿足跨語言、跨平臺(tái)進(jìn)行文本轉(zhuǎn)換、處理的要求。 1990年開始研發(fā),1994年正式公布。隨著計(jì)算機(jī)工作能力的增強(qiáng),Unicode也在面世以來的十多年里得到普及。但自從unicode2.0開始,unicode采用了與ISO 10646-1相同的字庫和字碼,ISO也承諾ISO10646將不會(huì)給超出0x10FFFF的UCS-4編碼賦值,使得兩者保持一致。
Unicode的編碼方式與ISO 10646的通用字符集(Universal Character Set,UCS)概念相對(duì)應(yīng),目前的用于實(shí)用的Unicode版本對(duì)應(yīng)于UCS-2,使用16位的編碼空間。也就是每個(gè)字符占用2個(gè)字節(jié),基本滿足各種語言的使用。實(shí)際上目前版本的Unicode尚未填充滿這16位編碼,保留了大量空間作為特殊使用或?qū)頂U(kuò)展。
UTFUnicode 的實(shí)現(xiàn)方式不同于編碼方式。
一個(gè)字符的Unicode編碼是確定的,但是在實(shí)際傳輸過程中,由于不同系統(tǒng)平臺(tái)的設(shè)計(jì)不一定一致,以及出于節(jié)省空間的目的,對(duì)Unicode編碼的實(shí)現(xiàn)方式有所不同。Unicode的實(shí)現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Translation Format,簡(jiǎn)稱為 UTF)。
² UTF-8: 8bit變長(zhǎng)編碼,對(duì)于大多數(shù)常用字符集(ASCII中0~127字符)它只使用單字節(jié),而對(duì)其它常用字符(特別是朝鮮和漢語會(huì)意文字),它使用3字節(jié)。
² UTF-16: 16bit編碼,是變長(zhǎng)碼,大致相當(dāng)于20位編碼,值在0到0x10FFFF之間,基本上就是unicode編碼的實(shí)現(xiàn),與CPU字序有關(guān)。
漢字編碼:
² GB2312字集是簡(jiǎn)體字集,全稱為GB2312(80)字集,共包括國(guó)標(biāo)簡(jiǎn)體漢字6763個(gè)。
² BIG5字集是臺(tái)灣繁體字集,共包括國(guó)標(biāo)繁體漢字13053個(gè)。
² GBK字集是簡(jiǎn)繁字集,包括了GB字集、BIG5字集和一些符號(hào),共包括21003個(gè)字符
² GB18030是國(guó)家制定的一個(gè)強(qiáng)制性大字集標(biāo)準(zhǔn),全稱為GB18030-2000,它的推出使?jié)h字集有了一個(gè)“大一統(tǒng)”的標(biāo)準(zhǔn)。
ANSI和Unicode big endia
我們?cè)赪indows系統(tǒng)中保存文本文件時(shí)通??梢赃x擇編碼為ANSI、Unicode、Unicode big endian和UTF-8,這里的ANSI和Unicode big endia是什么編碼呢?
² ANSI使用2個(gè)字節(jié)來代表一個(gè)字符的各種漢字延伸編碼方式,稱為ANSI編碼。 在簡(jiǎn)體中文系統(tǒng)下,ANSI編碼代表GB2312編碼,在日文操作系統(tǒng)下,ANSI編碼代表JIS編碼。
² Unicode big endiaUTF-8以字節(jié)為編碼單元,沒有字節(jié)序的問題。UTF-16以兩個(gè)字節(jié)為編碼單元,在解釋一個(gè)UTF-16文本前,首先要弄清楚每個(gè)編碼單元的字節(jié)序。 Unicode規(guī)范中推薦的標(biāo)記字節(jié)順序的方法是BOM(即Byte Order Mark)。 在UCS編碼中有一個(gè)叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應(yīng)該出現(xiàn)在實(shí)際傳輸中。 UCS規(guī)范建議我們?cè)趥鬏斪止?jié)流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。 這樣如果接收者收到FEFF,就表明這個(gè)字節(jié)流是Big-Endian的;如果收到FFFE,就表明這個(gè)字節(jié)流是Little-Endian的。 因此字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。 Windows就是使用BOM來標(biāo)記文本文件的編碼方式的。
4 為什么會(huì)亂碼?
亂碼是個(gè)老問題,字符在保存時(shí)的編碼格式如果和要顯示的編碼格式不一樣的話,就會(huì)出現(xiàn)亂碼問題。在我們的Web應(yīng)用中,從底層數(shù)據(jù)庫編碼、Web應(yīng)用程序編碼到HTML頁面編碼,如果有一項(xiàng)不一致的話,就會(huì)出現(xiàn)亂碼。 所以,解決亂碼問題說難也難說簡(jiǎn)單也簡(jiǎn)單,關(guān)鍵是讓交互系統(tǒng)之間編碼一致。
5 Java字符編碼
Java在運(yùn)行期一律以u(píng)nicode來存儲(chǔ)字符,這樣有利的支持了多語言環(huán)境。
Java在讀取文件的時(shí)候默認(rèn)是按照系統(tǒng)默認(rèn)語言(字符集)編碼來解碼文件,讀取和保存時(shí)候的編碼不一致也導(dǎo)致程序中參數(shù)值錯(cuò)誤,用FileInputStream類讀取文件可以指定編碼讀取。
Java在輸出到系統(tǒng)顯示時(shí),會(huì)把內(nèi)存中變量字符再通過系統(tǒng)默認(rèn)語言(字符集)編碼去轉(zhuǎn)換,所以在輸出過程中也會(huì)碰到一系列的編碼問題。
6 編碼JSP
這里我們主要是介紹JSP頁面的兩個(gè)重要屬性:pageEncoding、contentTypecharset
pageEncoding是jsp文件本身的編碼
contentType的charset是指服務(wù)器發(fā)送給客戶端時(shí)的內(nèi)容編碼
charset有兩個(gè)作用:一是JSP文件的編碼方式:在讀取JSP文件、生成JAVA類時(shí),源JSP文件中漢字的編碼;二是JSP輸出流的編碼方式:在執(zhí)行JSP時(shí),往response流里面寫入數(shù)據(jù)的編碼方式
當(dāng)應(yīng)用服務(wù)器將JSP編譯成.java文件時(shí),會(huì)根據(jù)pageEncoding的設(shè)定讀取jsp,結(jié)果是由指定的編碼方案翻譯成統(tǒng)一的UTF-8 JAVA源碼(即.java),如果pageEncoding設(shè)定錯(cuò)了,或沒有設(shè)定,出來的就是中文亂碼。
當(dāng)應(yīng)用服務(wù)器利用已經(jīng)編譯為二進(jìn)制的JSP(.class)輸出頁面時(shí),contentType這時(shí)則決定了輸出頁面的編碼。
jsp文件不像.java,.java在被編譯器讀入的時(shí)候默認(rèn)采用的是操作系統(tǒng)所設(shè)定的locale所對(duì)應(yīng)的編碼,比如中國(guó)大陸就是GBK,臺(tái)灣就是BIG5或者M(jìn)S950。而一般我們不管是在記事本還是在ue中寫代碼,如果沒有經(jīng)過特別轉(zhuǎn)碼的話,寫出來的都是本地編碼格式的內(nèi)容。所以編譯器采用的方法剛好可以讓虛擬機(jī)得到正確的資料。
但是jsp文件不是這樣,它沒有這個(gè)默認(rèn)轉(zhuǎn)碼過程,但是指定了pageEncoding就可以實(shí)現(xiàn)正確轉(zhuǎn)碼了。
舉個(gè)例子:
<%@ page contentType="text/html;charset=utf-8" %>
大都會(huì)打印出亂碼,因?yàn)槲逸斎氲摹澳愫脝帷笔莋bk的,但是服務(wù)器是否正確抓到“你好嗎”不得而知。
但是如果更改為
<%@ page contentType="text/html;charset=utf-8" pageEncoding="GBK"%>
這樣就服務(wù)器一定會(huì)是正確抓到“你好嗎”了。
7 有沒有萬金油?
J2EE應(yīng)用程序是運(yùn)行在J2EE容器中。在這個(gè)系統(tǒng)中,輸入途徑有很多種:一種是通過頁面表單打包成請(qǐng)求(request)發(fā)往服務(wù)器的;第二種是通過數(shù)據(jù)庫讀入;還有第3種輸入比較復(fù)雜,JSP在第一次運(yùn)行時(shí)總是被編譯成Servlet,JSP中常常包含中文字符,那么編譯使用javac時(shí),Java將根據(jù)默認(rèn)的操作系統(tǒng)編碼作為初始編碼。除非特別指定,如在Jbuilder/eclipse中可以指定默認(rèn)的字符集。
輸出途徑也有幾種:第一種是JSP頁面的輸出。由于JSP頁面已經(jīng)被編譯成Servlet,那么在輸出時(shí),也將根據(jù)操作系統(tǒng)的默認(rèn)編碼來選擇輸出編碼,除非指定輸出編碼方式;還有輸出途徑是數(shù)據(jù)庫,將字符串輸出到數(shù)據(jù)庫。
由此看來,一個(gè)J2EE系統(tǒng)的輸入輸出是非常復(fù)雜,而且是動(dòng)態(tài)變化的,而Java是跨平臺(tái)運(yùn)行的,在實(shí)際編譯和運(yùn)行中,都可能涉及到不同的操作系統(tǒng),如果任由Java自由根據(jù)操作系統(tǒng)來決定輸入輸出的編碼字符集,這將不可控制地出現(xiàn)亂碼。
正是由于Java的跨平臺(tái)特性,使得字符集問題必須由具體系統(tǒng)來統(tǒng)一解決,所以在一個(gè)Java應(yīng)用系統(tǒng)中,解決中文亂碼的根本辦法是明確指定整個(gè)應(yīng)用系統(tǒng)統(tǒng)一字符集。
指定統(tǒng)一字符集時(shí),到底是指定ISO8859_1 、GBK還是UTF-8呢?
1)如統(tǒng)一指定為ISO8859_1,因?yàn)槟壳按蠖鄶?shù)軟件都是西方人編制的,他們默認(rèn)的字符集就是ISO8859_1,包括操作系統(tǒng)Linux和數(shù)據(jù)庫MySQL等。這樣,如果指定Jive統(tǒng)一編碼為ISO8859_1,那么就有下面3個(gè)環(huán)節(jié)必須把握:
開發(fā)和編譯代碼時(shí)指定字符集為ISO8859_1。
運(yùn)行操作系統(tǒng)的默認(rèn)編碼必須是ISO8859_1,如Linux。
在JSP頭部聲明。
2)如果統(tǒng)一指定為GBK中文字符集,上述3個(gè)環(huán)節(jié)同樣需要做到,不同的是只能運(yùn)行在默認(rèn)編碼為GBK的操作系統(tǒng),如中文Windows。
統(tǒng)一編碼為ISO8859_1和GBK雖然帶來編制代碼的方便,但是各自只能在相應(yīng)的操作系統(tǒng)上運(yùn)行。但是也破壞了Java跨平臺(tái)運(yùn)行的優(yōu)越性,只在一定范圍內(nèi)行得通。例如,為了使得GBK編碼在linux上運(yùn)行,設(shè)置Linux編碼為GBK。
那么有沒有一種除了應(yīng)用系統(tǒng)以外不需要進(jìn)行任何附加設(shè)置的中文編碼根本解決方案呢?
Java/J2EE系統(tǒng)的統(tǒng)一編碼定義為UTF-8。UTF-8編碼是一種兼容所有語言的編碼方式,惟一比較麻煩的就是要找到應(yīng)用系統(tǒng)的所有出入口,然后使用UTF-8去“結(jié)扎”它。
一個(gè)J2EE應(yīng)用系統(tǒng)需要做下列幾步工作:
開發(fā)和編譯代碼時(shí)指定字符集為UTF-8。JBuilder和Eclipse都可以在項(xiàng)目屬性中設(shè)置。
使用過濾器,如果所有請(qǐng)求都經(jīng)過一個(gè)Servlet控制分配器,那么使用Servlet的filter執(zhí)行語句,將所有來自瀏覽器的請(qǐng)求(request)轉(zhuǎn)換為UTF-8,因?yàn)闉g覽器發(fā)過來的請(qǐng)求包根據(jù)瀏覽器所在的操作系統(tǒng)編碼,可能是各種形式編碼。關(guān)鍵一句:
request.setCharacterEncoding("UTF-8")
在JSP頭部聲明:
<%@ page contentType="text/html;charset= UTF-8" %>
在Jsp的html代碼中,聲明UTF-8:
<META http-equiv="Content-Type" CONTET="text/html; charset=utf-8">
設(shè)定數(shù)據(jù)庫連接方式是UTF-8。例如連接MYSQL時(shí)配置URL如下:
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
一般數(shù)據(jù)庫都可以通過管理設(shè)置設(shè)定UTF-8。其他和外界交互時(shí)能夠設(shè)定編碼時(shí)就設(shè)定UTF-8,例如讀取文件,操作XML等。
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI