java中文亂碼decode_Java中文亂碼處理

java編碼轉換過程

我們總是用一個java類文件和用戶進行最直接的交互(輸入、輸出),這些交互內容包含的文字可能會包含中文。無論這些java類是與數據庫交互,還是與前端頁面交互,他們的生命周期總是這樣的:

1、程序員在操作系統上通過編輯器編寫程序代碼并且以.java的格式保存操作系統中,這些文件我們稱之為源文件。

2、通過JDK中的javac.exe編譯這些源文件形成.class類。

3、直接運行這些類或者部署在WEB容器中運行,得到輸出結果。

這些過程是從宏觀上面來觀察的,了解這個肯定是不行的,我們需要真正來了解java是如何來編碼和被解碼的:

第一步:當我們用編輯器編寫java源文件,程序文件在保存時會采用操作系統默認的編碼格式(一般我們中文的操作系統采用的是GBK編碼格式)形成一個.java文件。java源文件是采用操作系統默認支持的file.encoding編碼格式保存的。下面代碼可以查看系統的file.encoding參數值。

System.out.println(System.getProperty("file.encoding"));

第二步:當我們使用javac.exe編譯我們的java文件時,JDK首先會確認它的編譯參數encoding來確定源代碼字符集,如果我們不指定該編譯參數,JDK首先會獲取操作系統默認的file.encoding參數,然后JDK就會把我們編寫的java源程序從file.encoding編碼格式轉化為JAVA內部默認的UNICODE格式放入內存中。

第三步:JDK將上面編譯好的且保存在內存中信息寫入class文件中,形成.class文件。此時.class文件是Unicode編碼的,也就是說我們常見的.class文件中的內容無論是中文字符還是英文字符,他們都已經轉換為Unicode編碼格式了。

在這一步中對對JSP源文件的處理方式有點兒不同:WEB容器調用JSP編譯器,JSP編譯器首先會查看JSP文件是否設置了文件編碼格式,如果沒有設置則JSP編譯器會調用調用JDK采用默認的編碼方式將JSP文件轉化為臨時的servlet類,然后再編譯為.class文件并保持到臨時文件夾中。

第四步:運行編譯的類:在這里會存在一下幾種情況

1、直接在console上運行。

2、JSP/Servlet類。

3、java類與數據庫之間。

這三種情況每種情況的方式都會不同,

1.Console上運行的類

這種情況下,JVM首先會把保存在操作系統中的class文件讀入到內存中,這個時候內存中class文件編碼格式為Unicode,然后JVM運行它。如果需要用戶輸入信息,則會采用file.encoding編碼格式對用戶輸入的信息進行編碼同時轉換為Unicode編碼格式保存到內存中。程序運行后,將產生的結果再轉化為file.encoding格式返回給操作系統并輸出到界面去。整個流程如下:

在上面整個流程中,凡是涉及的編碼轉換都不能出現錯誤,否則將會產生亂碼。

2.Servlet類

由于JSP文件最終也會轉換為servlet文件(只不過存儲的位置不同而已),所以這里我們也將JSP文件納入其中。

當用戶請求Servlet時,WEB容器會調用它的JVM來運行Servlet。首先JVM會把servlet的class加載到內存中去,內存中的servlet代碼是Unicode編碼格式的。然后JVM在內存中運行該Servlet,在運行過程中如果需要接受從客戶端傳遞過來的數據(如表單和URL傳遞的數據),則WEB容器會接受傳入的數據,在接收過程中如果程序設定了傳入參數的的編碼則采用設定的編碼格式,如果沒有設置則采用默認的ISO-8859-1編碼格式,接收的數據后JVM會將這些數據進行編碼格式轉換為Unicode并且存入到內存中。運行Servlet后產生輸出結果,同時這些輸出結果的編碼格式仍然為Unicode。緊接著WEB容器會將產生的Unicode編碼格式的字符串直接發送置客戶端,如果程序指定了輸出時的編碼格式,則按照指定的編碼格式輸出到瀏覽器,否則采用默認的ISO-8859-1編碼格式。整個過程流程圖如下:

3.數據庫部分

我們知道java程序與數據庫的連接都是通過JDBC驅動程序來連接的,而JDBC驅動程序默認的是ISO-8859-1編碼格式的,也就是說我們通過java程序向數據庫傳遞數據時,JDBC首先會將Unicode編碼格式的數據轉換為ISO-8859-1的編碼格式,然后在存儲在數據庫中,即在數據庫保存數據時,默認格式為ISO-8859-1。

編碼&解碼

在上篇博客中LZ闡述了三個渠道的編碼轉換過程,下面LZ將結束java在那些場合需要進行編碼和解碼操作,并詳序中間的過程,進一步掌握java的編碼和解碼過程。在java中主要有四個場景需要進行編碼解碼操作:

1:I/O操作

2:內存

3:數據庫

4:javaWeb

下面主要介紹前面兩種場景,數據庫部分只要設置正確編碼格式就不會有什么問題,javaWeb場景過多需要了解URL、get、POST的編碼,servlet的解碼,所以javaWeb場景下節LZ介紹。

I/O操作

在前面LZ就提過亂碼問題無非就是轉碼過程中編碼格式的不統一產生的,比如編碼時采用UTF-8,解碼采用GBK,但最根本的原因是字符到字節或者字節到字符的轉換出問題了,而這中情況的轉換最主要的場景就是I/O操作的時候。當然I/O操作主要包括網絡I/O(也就是javaWeb)和磁盤I/O。網絡I/O下節介紹。

首先我們先看I/O的編碼操作。

InputStream為字節輸入流的所有類的超類,Reader為讀取字符流的抽象類。java讀取文件的方式分為按字節流讀取和按字符流讀取,其中InputStream、Reader是這兩種讀取方式的超類。

按字節

我們一般都是使用InputStream.read()方法在數據流中讀取字節(read()每次都只讀取一個字節,效率非常慢,我們一般都是使用read(byte[])),然后保存在一個byte[]數組中,最后轉換為String。在我們讀取文件時,讀取字節的編碼取決于文件所使用的編碼格式,而在轉換為String過程中也會涉及到編碼的問題,如果兩者之間的編碼格式不同可能會出現問題。例如存在一個問題test.txt編碼格式為UTF-8,那么通過字節流讀取文件時所獲得的數據流編碼格式就是UTF-8,而我們在轉化成String過程中如果不指定編碼格式,則默認使用系統編碼格式(GBK)來解碼操作,由于兩者編碼格式不一致,那么在構造String過程肯定會產生亂碼,如下:

File file = new File("C:\\test.txt");

InputStream input= newFileInputStream(file);

StringBuffer buffer= newStringBuffer();byte[] bytes = new byte[1024];for(int n ; (n = input.read(bytes))!=-1; ){

buffer.append(new String(bytes,0,n));

}

System.out.println(buffer);

輸出結果:锘挎垜鏄?cm

test.txt中的內容為:我是 cm。

要想不出現亂碼,在構造String過程中指定編碼格式,使得編碼解碼時兩者編碼格式保持一致即可:

buffer.append(new String(bytes,0,n,"UTF-8"));

按字符

其實字符流可以看做是一種包裝流,它的底層還是采用字節流來讀取字節,然后它使用指定的編碼方式將讀取字節解碼為字符。在java中Reader是讀取字符流的超類。所以從底層上來看按字節讀取文件和按字符讀取沒什么區別。在讀取的時候字符讀取每次是讀取留個字節,字節流每次讀取一個字節。

字節&字符轉換

字節轉換為字符一定少不了InputStreamReader。API解釋如下:InputStreamReader 是字節流通向字符流的橋梁:它使用指定的?charset?讀取字節并將其解碼為字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集。 每次調用 InputStreamReader 中的一個 read() 方法都會導致從底層輸入流讀取一個或多個字節。要啟用從字節到字符的有效轉換,可以提前從底層流讀取更多的字節,使其超過滿足當前讀取操作所需的字節。API解釋非常清楚,InputStreamReader在底層讀取文件時仍然采用字節讀取,讀取字節后它需要根據一個指定的編碼格式來解析為字符,如果沒有指定編碼格式則采用系統默認編碼格式。

String file = "C:\\test.txt";

String charset= "UTF-8";//寫字符換轉成字節流

FileOutputStream outputStream = newFileOutputStream(file);

OutputStreamWriter writer= newOutputStreamWriter(outputStream, charset);try{

writer.write("我是 cm");

}finally{

writer.close();

}//讀取字節轉換成字符

FileInputStream inputStream = newFileInputStream(file);

InputStreamReader reader= newInputStreamReader(

inputStream, charset);

StringBuffer buffer= newStringBuffer();char[] buf = new char[64];int count = 0;try{while ((count = reader.read(buf)) != -1) {

buffer.append(buf,0, count);

}

}finally{

reader.close();

}

System.out.println(buffer);

內存

首先我們看下面這段簡單的代碼

String s = "我是 cm";byte[] bytes =s.getBytes();

String s1= new String(bytes,"GBK");

String s2= new String(bytes);

在這段代碼中我們看到了三處編碼轉換過程(一次編碼,兩次解碼)。先看String.getTytes():

public byte[] getBytes() {return StringCoding.encode(value, 0, value.length);

}

內部調用StringCoding.encode()方法操作:

static byte[] encode(char[] ca, int off, intlen) {

String csn=Charset.defaultCharset().name();try{//use charset name encode() variant which provides caching.

returnencode(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);return null;

}

}

encode(char[] paramArrayOfChar, int paramInt1, int paramInt2)方法首先調用系統的默認編碼格式,如果沒有指定編碼格式則默認使用ISO-8859-1編碼格式進行編碼操作,進一步深入如下:

String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;

同樣的方法可以看到new String 的構造函數內部是調用StringCoding.decode()方法:

public String(byte bytes[], int offset, intlength, Charset charset) {if (charset == null)throw new NullPointerException("charset");

checkBounds(bytes, offset, length);this.value =StringCoding.decode(charset, bytes, offset, length);

}

decode方法和encode對編碼格式的處理是一樣的。

對于以上兩種情況我們只需要設置統一的編碼格式一般都不會產生亂碼問題。

編碼&編碼格式

首先先看看java編碼類圖[1]

首先根據指定的chart設置ChartSet類,然后根據ChartSet創建ChartSetEncoder對象,最后再調用 CharsetEncoder.encode 對字符串進行編碼,不同的編碼類型都會對應到一個類中,實際的編碼過程是在這些類中完成的。下面時序圖展示詳細的編碼過程:

通過這編碼的類圖和時序圖可以了解編碼的詳細過程。下面將通過一段簡單的代碼對ISO-8859-1、GBK、UTF-8編碼

public classTest02 {public static void main(String[] args) throwsUnsupportedEncodingException {

String string= "我是 cm";

Test02.printChart(string.toCharArray());

Test02.printChart(string.getBytes("ISO-8859-1"));

Test02.printChart(string.getBytes("GBK"));

Test02.printChart(string.getBytes("UTF-8"));

}/*** char轉換為16進制*/

public static void printChart(char[] chars){for(int i = 0 ; i < chars.length ; i++){

System.out.print(Integer.toHexString(chars[i])+ " ");

}

System.out.println("");

}/*** byte轉換為16進制*/

public static void printChart(byte[] bytes){for(int i = 0 ; i < bytes.length ; i++){

String hex= Integer.toHexString(bytes[i] & 0xFF);if (hex.length() == 1) {

hex= '0' +hex;

}

System.out.print(hex.toUpperCase()+ " ");

}

System.out.println("");

}

}

-------------------------outPut:6211 662f 20 636d

3F 3F20 636D

CE D2 CA C720 636D

E688 91 E6 98 AF 20 63 6D

通過程序我們可以看到“我是 cm”的結果為:

char[]:6211 662f 20 636d

ISO-8859-1:3F 3F 20 636D

GBK:CE D2 CA C720 636D

UTF-8:E6 88 91 E6 98 AF 20 63 6D

圖如下:

編碼&解碼

通過下圖我們可以了解在javaWeb中有哪些地方有轉碼:

用戶想服務器發送一個HTTP請求,需要編碼的地方有url、cookie、parameter,經過編碼后服務器接受HTTP請求,解析HTTP請求,然后對url、cookie、parameter進行解碼。在服務器進行業務邏輯處理過程中可能需要讀取數據庫、本地文件或者網絡中的其他文件等等,這些過程都需要進行編碼解碼。當處理完成后,服務器將數據進行編碼后發送給客戶端,瀏覽器經過解碼后顯示給用戶。在這個整個過程中涉及的編碼解碼的地方較多,其中最容易出現亂碼的位置就在于服務器與客戶端進行交互的過程。

上面整個過程可以概括成這樣,頁面編碼數據傳遞給服務器,服務器對獲得的數據進行解碼操作,經過一番業務邏輯處理后將最終結果編碼處理后傳遞給客戶端,客戶端解碼展示給用戶。所以下面我就請求對javaweb的編碼&解碼進行闡述。

請求

客戶端想服務器發送請求無非就通過四中情況:

1、URL方式直接訪問。

2、頁面鏈接。

3、表單get提交

4、表單post提交

URL方式

對于URL,如果該URL中全部都是英文的那倒是沒有什么問題,如果有中文就要涉及到編碼了。如何編碼?根據什么規則來編碼?又如何來解碼呢?下面LZ將一一解答!首先看URL的組成部分:

在這URL中瀏覽器將會對path和parameter進行編碼操作。為了更好地解釋編碼過程,使用如下URL

http://127.0.0.1:8080/perbank/我是cm?name=我是cm

將以上地址輸入到瀏覽器URL輸入框中,通過查看http 報文頭信息我們可以看到瀏覽器是如何進行編碼的。下面是IE、Firefox、Chrome三個瀏覽器的編碼情況:

可以看到各大瀏覽器對“我是”的編碼情況如下:

path部分

Query String

Firefox

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

Chrome

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

IE

E6 88 91 E6 98 AF

CE D2 CA C7

查閱上篇博客的編碼可知對于path部分Firefox、chrome、IE都是采用UTF-8編碼格式,對于Query String部分Firefox、chrome采用UTF-8,IE采用GBK。至于為什么會加上%,這是因為URL的編碼規范規定瀏覽器將ASCII字符非 ASCII 字符按照某種編碼格式編碼成 16 進制數字然后將每個 16 進制表示的字節前加上“%”。

當然對于不同的瀏覽器,相同瀏覽器不同版本,不同的操作系統等環境都會導致編碼結果不同,上表某一種情況,對于URL編碼規則下任何結論都是過早的。由于各大瀏覽器、各個操作系統對URL的URI、QueryString編碼都可能存在不同,這樣對服務器的解碼勢必會造成很大的困擾,下面我們將已tomcat,看tomcat是如何對URL進行解碼操作的。

解析請求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,這個方法把傳過來的 URL 的 byte[] 設置到 org.apache.coyote.Request 的相應的屬性中。這里的 URL 仍然是 byte 格式,轉成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:

protected voidconvertURI(MessageBytes uri, Request request)throwsException {

ByteChunk bc=uri.getByteChunk();int length =bc.getLength();

CharChunk cc=uri.getCharChunk();

cc.allocate(length,-1);

String enc= connector.getURIEncoding(); //獲取URI解碼集

if (enc != null) {

B2CConverter conv=request.getURIConverter();try{if (conv == null) {

conv= newB2CConverter(enc);

request.setURIConverter(conv);

}

}catch(IOException e) {...}if (conv != null) {try{

conv.convert(bc, cc, cc.getBuffer().length-cc.getEnd());

uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());return;

}catch(IOException e) {...}

}

}//Default encoding: fast conversion

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);

}

從上面的代碼可知,對URI的解碼操作是首先獲取Connector的解碼集,該配置在server.xml中

如果沒有定義則會采用默認編碼ISO-8859-1來解析。

對于Query String部分,我們知道無論我們是通過get方式還是POST方式提交,所有的參數都是保存在Parameters,然后我們通過request.getParameter,解碼工作就是在第一次調用getParameter方法時進行的。在getParameter方法內部它調用org.apache.catalina.connector.Request 的 parseParameters 方法,這個方法將會對傳遞的參數進行解碼。下面代碼只是parseParameters方法的一部分:

//獲取編碼

String enc =getCharacterEncoding();//獲取ContentType 中定義的 Charset

boolean useBodyEncodingForURI =connector.getUseBodyEncodingForURI();if (enc != null) { //如果設置編碼不為空,則設置編碼為enc

parameters.setEncoding(enc);if (useBodyEncodingForURI) { //如果設置了Chartset,則設置queryString的解碼為ChartSet

parameters.setQueryStringEncoding(enc);

}

}else { //設置默認解碼方式

parameters.setEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);if(useBodyEncodingForURI) {

parameters.setQueryStringEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);

}

}

從上面代碼可以看出對query String的解碼格式要么采用設置的ChartSet要么采用默認的解碼格式ISO-8859-1。注意這個設置的ChartSet是在 http Header中定義的ContentType,同時如果我們需要改指定屬性生效,還需要進行如下配置:

上面部分詳細介紹了URL方式請求的編碼解碼過程。其實對于我們而言,我們更多的方式是通過表單的形式來提交。

表單GET

我們知道通過URL方式提交數據是很容易產生亂碼問題的,所以我們更加傾向于通過表單形式。當用戶點擊submit提交表單時,瀏覽器會更加設定的編碼來編碼數據傳遞給服務器。通過GET方式提交的數據都是拼接在URL后面(可以當做query String??)來提交的,所以tomcat服務器在進行解碼過程中URIEncoding就起到作用了。tomcat服務器會根據設置的URIEncoding來進行解碼,如果沒有設置則會使用默認的ISO-8859-1來解碼。假如我們在頁面將編碼設置為UTF-8,而URIEncoding設置的不是或者沒有設置,那么服務器進行解碼時就會產生亂碼。這個時候我們一般可以通過new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8") 的形式來獲取正確數據。

表單POST

對于POST方式,它采用的編碼也是由頁面來決定的即contentType。當我通過點擊頁面的submit按鈕來提交表單時,瀏覽器首先會根據ontentType的charset編碼格式來對POST表單的參數進行編碼然后提交給服務器,在服務器端同樣也是用contentType中設置的字符集來進行解碼(這里與get方式就不同了),這就是通過POST表單提交的參數一般而言都不會出現亂碼問題。當然這個字符集編碼我們是可以自己設定的:request.setCharacterEncoding(charset) 。

我們知道JSP頁面是需要轉換為servlet的,在轉換過程中肯定是要進行編碼的。在JSP轉換為servlet過程中下面一段代碼起到至關重要的作用。

在上面代碼中有兩個地方存在編碼:pageEncoding、contentType的charset。其中pageEncoding是jsp文件本身的編碼,而contentType的charset是指服務器發送給客戶端時的內容編碼。

jsp在轉換為Servlet的過程中是需要經過主要的三次編碼轉換過程(除去數據庫編碼轉換、頁面參數輸入編碼轉換):

第一次:轉換為.java文件;

第二次:轉換為.class文件;

第三次:業務邏輯處理后輸出。

第一階段

JVM將JSP編譯為.jsp文件。在這個過程中pageEncoding就起到作用了,JVM首先會獲取pageEncoding的值,如果該值存在則采用它設定的編碼來編譯,否則則采用file.encoding編碼來編譯。

第二階段

JVM將.java文件轉換為.class文件。在這個過程就與任何編碼的設置都沒有關系了,不管JSP采用了什么樣的編碼格式都將無效。經過這個階段后.jsp文件就轉換成了統一的Unicode格式的.class文件了。

第三階段

后臺經過業務邏輯處理后將產生的結果輸出到客戶端。在這個過程中contentType的charset就發揮了功效。如果設置了charset則瀏覽器就會使用指定的編碼格式進行解碼,否則采用默認的ISO-8859-1編碼格式進行解碼處理。

流程如如下:

我們主要通過兩種形式提交向服務器發送請求:URL、表單。而表單形式一般都不會出現亂碼問題,亂碼問題主要是在URL上面。通過前面幾篇博客的介紹我們知道URL向服務器發送請求編碼過程實在是實在太混亂了。不同的操作系統、不同的瀏覽器、不同的網頁字符集,將導致完全不同的編碼結果。如果程序員要把每一種結果都考慮進去,是不是太恐怖了?有沒有辦法,能夠保證客戶端只用一種編碼方法向服務器發出請求?

有!這里我主要提供以下幾種方法

一、javascript

使用javascript編碼不給瀏覽器插手的機會,編碼之后再向服務器發送請求,然后在服務器中解碼。在掌握該方法的時候,我們需要料及javascript編碼的三個方法:escape()、encodeURI()、encodeURIComponent()。

escape

采用SIO Latin字符集對指定的字符串進行編碼。所有非ASCII字符都會被編碼為%xx格式的字符串,其中xx表示該字符在字符集中所對應的16進制數字。例如,格式對應的編碼為%20。它對應的解碼方法為unescape()。

事實上escape()不能直接用于URL編碼,它的真正作用是返回一個字符的Unicode編碼值。比如上面“我是cm”的結果為%u6211%u662Fcm,其中“我”對應的編碼為6211,“是”的編碼為662F,“cm”編碼為cm。

注意,escape()不對"+"編碼。但是我們知道,網頁在提交表單的時候,如果有空格,則會被轉化為+字符。服務器處理數據的時候,會把+號處理成空格。所以,使用的時候要小心。

encodeURI

對整個URL進行編碼,它采用的是UTF-8格式輸出編碼后的字符串。不過encodeURI除了ASCII編碼外對于一些特殊的字符也不會進行編碼如:! @ # $& * ( ) = : / ; ? + '。

encodeURIComponent

把URI字符串采用UTF-8編碼格式轉化成escape格式的字符串。相對于encodeURI,encodeURIComponent會更加強大,它會對那些在encodeURI()中不被編碼的符號(; / ? : @ & = + $ , #)統統會被編碼。但是encodeURIComponent只會對URL的組成部分進行個別編碼,而不用于對整個URL進行編碼。對應解碼函數方法decodeURIComponent。

當然我們一般都是使用encodeURI方來進行編碼操作。所謂的javascript兩次編碼后臺兩次解碼就是使用該方法。javascript解決該問題有一次轉碼、兩次轉碼兩種解決方法。

一次轉碼

javascript轉碼:

var url = '/ShowMoblieQRCode.servlet?name=我是cm';

window.location.href= encodeURI(url);

后臺處理:

String name = request.getParameter("name");

System.out.println("前臺傳入參數:" +name);

name= new String(name.getBytes("ISO-8859-1"),"UTF-8");

System.out.println("經過解碼后參數:" + name);

輸出結果:

前臺傳入參數:??????cm

經過解碼后參數:我是cm

二次轉碼

var url = '/ShowMoblieQRCode.servlet?name=我是cm';

window.location.href= encodeURI(encodeURI(url));

后臺處理:

String name = request.getParameter("name");

System.out.println("前臺傳入參數:" +name);

name= URLDecoder.decode(name,"UTF-8");

System.out.println("經過解碼后參數:" + name);

輸出結果:

前臺傳入參數:E68891E698AFcm

經過解碼后參數:我是cm

filter

使用過濾器,過濾器LZ提供兩種,第一種設置編碼,第二種直接在過濾器中進行解碼操作。

過濾器1

該過濾器是直接設置request的編碼格式的。

public class CharacterEncoding implementsFilter {privateFilterConfig config ;

String encoding= null;public voiddestroy() {

config= null;

}public voiddoFilter(ServletRequest request, ServletResponse response,

FilterChain chain)throwsIOException, ServletException {

request.setCharacterEncoding(encoding);

chain.doFilter(request, response);

}public void init(FilterConfig config) throwsServletException {this.config =config;//獲取配置參數

String str = config.getInitParameter("encoding");if(str!=null){

encoding=str;

}

}

}

配置:

chineseEncoding

com.test.filter.CharacterEncoding

encoding

utf-8

chineseEncoding

/*

過濾器2

該過濾器在處理方法中將參數直接進行解碼操作,然后將解碼后的參數重新設置到request的attribute中。

public class CharacterEncoding implementsFilter {protectedFilterConfig filterConfig ;

String encoding= null;public voiddestroy() {this.filterConfig = null;

}/*** 初始化*/

public voidinit(FilterConfig filterConfig) {this.filterConfig =filterConfig;

}/*** 將 inStr 轉為 UTF-8 的編碼形式

*

*@paraminStr 輸入字符串

*@returnUTF - 8 的編碼形式的字符串

*@throwsUnsupportedEncodingException*/

private String toUTF(String inStr) throwsUnsupportedEncodingException {

String outStr= "";if (inStr != null) {

outStr= new String(inStr.getBytes("iso-8859-1"), "UTF-8");

}returnoutStr;

}/*** 中文亂碼過濾處理*/

public voiddoFilter(ServletRequest servletRequest,

ServletResponse servletResponse, FilterChain chain)throwsIOException,

ServletException {

HttpServletRequest request=(HttpServletRequest) servletRequest;

HttpServletResponse response=(HttpServletResponse) servletResponse;//獲得請求的方式 (1.post or 2.get), 根據不同請求方式進行不同處理

String method =request.getMethod();//1. 以 post 方式提交的請求 , 直接設置編碼為 UTF-8

if (method.equalsIgnoreCase("post")) {try{

request.setCharacterEncoding("UTF-8");

}catch(UnsupportedEncodingException e) {

e.printStackTrace();

}

}//2. 以 get 方式提交的請求

else{//取出客戶提交的參數集

Enumeration paramNames =request.getParameterNames();//遍歷參數集取出每個參數的名稱及值

while(paramNames.hasMoreElements()) {

String name= paramNames.nextElement(); //取出參數名稱

String values[] = request.getParameterValues(name); //根據參數名稱取出其值//如果參數值集不為空

if (values != null) {//遍歷參數值集

for (int i = 0; i < values.length; i++) {try{//回圈依次將每個值調用 toUTF(values[i]) 方法轉換參數值的字元編碼

String vlustr =toUTF(values[i]);

values[i]=vlustr;

}catch(UnsupportedEncodingException e) {

e.printStackTrace();

}

}//將該值以屬性的形式藏在 request

request.setAttribute(name, values);

}

}

}//設置響應方式和支持中文的字元集

response.setContentType("text/html;charset=UTF-8");//繼續執行下一個 filter, 無一下個 filter 則執行請求

chain.doFilter(request, response);

}

}

配置:

chineseEncoding

com.test.filter.CharacterEncoding

chineseEncoding

/*

其他

1、設置pageEncoding、contentType

2、設置tomcat的URIEncoding

在默認情況下,tomcat服務器使用的是ISO-8859-1編碼格式來編碼的,URIEncoding參數對get請求的URL進行編碼,所以我們只需要在tomcat的server.xml文件的標簽中加上URIEncoding="utf-8"即可。

,請尊重作者辛勤勞動成果,轉載說明出處.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457434.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457434.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457434.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【設計模式 05】工廠方法模式

工廠方法模式 define an interface or abstract class for creating an object but let the subclasses decide which class to instantiate. 參考&#xff1a; refactoringguru | factory-methodjavatpoint | factory-method-design-pattern博客園| 工廠方法 簡單工廠的問題 …

[C++]宏定義#define A B C

C關于宏定義的用法&#xff0c;有形如#define A B C的格式&#xff0c;此時B和C都是已知的字符串常量. 在宏定義中. 可以把兩個常量字符串連在一起 如果#define A "a" 那么宏定義#define STRING A "bc" 就相當于 #define STRING "abc" 這里&…

LinkedList類源碼淺析(二)

1、上一節介紹了LinkedList的幾個基本的方法&#xff0c;其他方法類似&#xff0c;就不一一介紹&#xff1b; 現在再來看一個刪除的方法&#xff1a;remove(Object o) remove方法接受一個Object參數&#xff0c;這里需要對參數做空與非空處理&#xff1b; 但是刪除一個Object元…

【設計模式 06】原型模式(克隆??)

原型模式(clone?) Prototype pattern refers to creating duplicate object while keeping performance in mind. This type of design pattern comes under creational pattern as this pattern provides one of the best ways to create an object. 參考&#xff1a; tutori…

2016OSC源創會年終盛典-綜合技術專場-張小剛

2019獨角獸企業重金招聘Python工程師標準>>> 綜合技術專場 講師/SPEAKERS 張小剛 網易云負載均衡項目負責人 《網易蜂巢負載均衡技術實踐》從網易蜂巢中的實踐出發&#xff0c;分享網易蜂巢負載均衡服務從無到有&#xff0c;從私有云到公有云過程中的技術實踐。重點…

python策略模式包含角色_詳解Python設計模式之策略模式

雖然設計模式與語言無關&#xff0c;但這并不意味著每一個模式都能在每一門語言中使用。《設計模式&#xff1a;可復用面向對象軟件的基礎》一書中有 23 個模式&#xff0c;其中有 16 個在動態語言中“不見了&#xff0c;或者簡化了”。1、策略模式概述策略模式&#xff1a;定義…

mysql 日期

數據類型 數據類型格式date YYYY-MM-DD datetime YYYY-MM-DD HH:MM:SS timestamp YYYY-MM-DD HH:MM:SS year YYYY 或 YY 具體實現的函數 1、now() 返回當前的日期和時間 SELECT NOW(); 2、curdate() 返回當前的日期 SELECT CURdate(); 3、curtime&#xff08;&#xff09;返回當…

【Go】panic: reflect: call of reflect.Value.FieldByName on ptr Value

產生原因 調用 FieldByName()方法時&#xff0c;調用者與預期類型不相符。 // 錯誤代碼 func setNewArticleInfoToCache(article *Article) {fields : []string{"Title", "Abstract", "ID", "AuthorID", "CreateTime",}im…

超完整的 Chrome 瀏覽器客戶端調試大全

2019獨角獸企業重金招聘Python工程師標準>>> 引言 “工欲善其事&#xff0c;必先利其器” 沒錯&#xff0c;這句話個人覺得說的特別有道理&#xff0c;舉個例子來說吧&#xff0c;厲害的化妝師都有一套非常專業的刷子&#xff0c;散粉刷負責定妝&#xff0c;眼影刷負…

PHP 獲取服務器詳細信息【轉】

碰到此問題&#xff0c;做下記錄 獲取系統類型及版本號&#xff1a; php_uname() (例&#xff1a;Windows NT COMPUTER 5.1 build 2600)只獲取系統類型&#xff1a; php_uname(s) (或&#xff1…

HIVE攻略 JFK_Hive安裝及使用攻略

目錄Hive的安裝Hive的基本使用:CRUDHive交互式模式數據導入數據導出Hive查詢HiveQLHive視圖Hive分區表1. Hive的安裝系統環境裝好hadoop的環境后&#xff0c;我們可以把Hive裝在namenode機器上(c1)。hadoop的環境&#xff0c;請參考&#xff1a;讓Hadoop跑在云端系列文章&#…

MySQL 為什么用索引,為什么是 B+樹,怎么用索引

MySQL 索引 A database index is a data structure that improves the speed of operations in a table. Indexes can be created using one or more columns, providing the basis for both rapid random lookups and efficient ordering of access to records. 為什么需要索…

頁面加載完畢執行多個JS函數

通常我們需要在打開頁面時加載腳本&#xff0c;這些腳本必須在頁面加載完畢后才可以執行&#xff0c;因為這時候DOM才完整&#xff0c;可以利用window.onload確保這一點&#xff0c;如&#xff1a;window.οnlοadfirstFunction;這腳本的意思是在頁面完畢后執行firstFunction函…

Servlet 生命周期、工作原理

Servlet 生命周期&#xff1a;Servlet 加載--->實例化--->服務--->銷毀。init&#xff08;&#xff09;&#xff1a;在Servlet的生命周期中&#xff0c;僅執行一次init()方法。它是在服務器裝入Servlet時執行的&#xff0c;負責初始化Servlet對象。可以配置服務器&…

【Go 并發控制】上下文 context 源碼

Context 在 Go 服務中&#xff0c;往往由一個獨立的 goroutine 去處理一次請求&#xff0c;但在這個 goroutine 中&#xff0c;可能會開啟別的 goroutine 去執行一些具體的事務&#xff0c;如數據庫&#xff0c;RPC 等&#xff0c;同時&#xff0c;這一組 goroutine 可能還需要…

js設置全局變量ajax中賦值

js設置全局變量&#xff0c;在ajax中給予賦值賦值不上問題解決方案 方案一、 //在全局或某個需要的函數內設置Ajax異步為false&#xff0c;也就是同步. $.ajaxSetup({async : false}); //然后再進行你的Ajax操作 $.post(地址, 參數, function(data, status) { if (status &q…

iOS開發UI篇—模仿ipad版QQ空間登錄界面

一、實現和步驟 1.一般ipad項目在命名的時候可以加一個HD,標明為高清版 2.設置項目的文件結構&#xff0c;分為home和login兩個部分 3.登陸界面的設置 &#xff08;1&#xff09;設置第一個控制器和自定義的控制器類&#xff08;登陸&#xff09;關聯 &#xff08;2&#xff09…

click傳值vue_對vue下點擊事件傳參和不傳參的區別詳解

如下所示&#xff1a;{{btn_text1}}{{btn_text2}}var _vm new Vue({data : {btn_text1 : 點擊1 ,btn_text2 : 點擊2},methods : {test_click1 : function (e) {console.log(test_click1--------------------------) ;console.log(e) ;// 輸出結果&#xff1a;MouseEvent {isTr…

【Golang 源碼】sync.Map 源碼詳解

sync.Map 不安全的 map go 中原生的 map 不是并發安全的&#xff0c;多個 goroutine 并發地去操作一個 map 會拋出一個 panic package main import "fmt" func main() {m : map[string]int {"1": 1, "2": 2,}// 并發寫for i : 0; i < 100;…

oracle中scn(系統改變號)

系統scn&#xff1a; select checkpoint_change# from v$database; 文件scn&#xff1a; select name,checkpoint_change# from v$datafile; 結束scn&#xff1a; select name,last_change# from v$datafile; 數據文件頭部scn…