JSP亂碼雜談
06-20
看一個完整的註冊功能、查詢所有用戶並展現功能(數據寫入到資料庫中)。展現頁面全部由.jsp組成,與資料庫連接通過.java文件實現,web容器是Tomcat。sqlserver 2005資料庫,創建資料庫及表的.sql語句如下:USE [abc]GO/****** 對象: Table [dbo].[userinfo2] 腳本日期: 03/12/2012 08:46:39 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [dbo].[userinfo2]([userid] [int] IDENTITY(1,1) NOT NULL,[username] [varchar](50) NULL,[password] [varchar](50) NULL,[phone] [varchar](50) NULL,[realname] [nchar](10) NULL,CONSTRAINT [PK_userinfo3] PRIMARY KEY CLUSTERED([userid] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]GOSET ANSI_PADDING OFFGO取得與資料庫的連接及註冊的方法。UserInfo2Dao.javapackage com.laolu.dao;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;public class UserInfo2Dao {/*** @author huangyingchang* @功能:取得資料庫連接* @return connection*/public Connection getconn() {Connection connection = null;String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";String url = "jdbc:sqlserver://localhost:1433;DataBaseName=abc";try {Class.forName(driver);connection = DriverManager.getConnection(url, "sa", "sasa");} catch (Exception e) {e.printStackTrace();}System.out.println("資料庫連接成功,獲得的連接為:" + connection);return connection;}/*** @功能:用於測試資料庫是否連接成功* @param args*/public static void main(String[] args) {UserInfo2Dao u = new UserInfo2Dao();u.getconn();}/*** @author HuangYingChang* @param username* @param password* @param phone* @param realname* @return i* @功有:往資料庫中增加用戶(註冊用戶)*/public boolean addUser(String username, String password, String phone,String realname) {Connection connection = this.getconn();Statement statement;boolean flag = false;String sql = "insert into userinfo2 values("" + username + "",""+ password + "","" + phone + "","" + realname + "")";try {statement = connection.createStatement();int i = statement.executeUpdate(sql);if (i > 0) {flag = true;}statement.close();connection.close();} catch (SQLException e) {e.printStackTrace();}return flag;}/*** @功能:查詢出數據庫中所有的用戶* @return*/public ResultSet findUser(){//Object obj;Connection connection = this.getconn();Statement statement;ResultSet rs = null;String sql = "select * from userinfo2";try {statement = connection.createStatement();rs = statement.executeQuery(sql);/*while (rs.next()) {String username = rs.getString("username");String password = rs.getString("password");String phone = rs.getString("phone");String realname = rs.getString("realname");}*/} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}return rs;}}註冊頁面:add.jsp<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><base href="<%=basePath%>"><title>add.jsp</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css">--><script type="text/javascript">function cf(){if(addform.username.value == ""){alert("用戶名不能為空");return false;}else if(addform.password.value == ""){alert("密碼不能為空");return false;}else if(addform.reps.value == ""){alert("確認密碼不能為空");return false;}else if(addform.password.value != addform.reps.value){alert("兩次輸入的密碼不一致");}else if(addform.phone.value == ""){alert("手機號碼不能為空");}else if(addform.phone.value.length != 11){alert("手機號碼必須為11位");}else if(addform.realname.value == ""){alert("真實姓名不能為空");}}</script></head><body><form action="ch2/doadd.jsp" method="get" name="addform" onsubmit="return cf()">用戶名:<input type="text" name="username"><br/>密碼:<input type="password" name="password"><br/>確認密碼:<input type="password" name="reps"><br/>真實姓名:<input type="text" name="realname"><br/>電話:<input type="text" name="phone"><br/><input type="submit" value="註冊"></form></body></html>對註冊頁面提交的表單進行處理(一定要分清楚客戶端是以get方式還是post方式提交表單)。doadd.jsp:<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@page import="com.laolu.dao.UserInfo2Dao"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><base href="<%=basePath%>"><title>doadd.jsp</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css">--></head><body><%/*如果客戶端的表單提交方式使用get,提交中文時會亂碼,需要手動使用String name=new String(request.getParameter("name").getBytes("ISO-8859-1"),"GB2312");轉化編碼*///post表單提交方式,只需用下面二句,則可避免中文提交亂碼問題//request.setCharacterEncoding("UTF-8");// String username = request.getParameter("username");//以get方式提交時,處理中文參數的方法String uname = request.getParameter("username");//以ISO8859-1的方式進行編碼byte []b = uname.getBytes("ISO8859-1"); //jsp內部機制就是通過ISO8859-1進行解碼的String username = new String(b,"UTF-8");String password = request.getParameter("password");String phone = request.getParameter("phone");//處理中文亂碼String rn = request.getParameter("realname");byte b2[] = rn.getBytes("ISO8859-1");String realname = new String(b2,"UTF-8");out.println(username);UserInfo2Dao user = new UserInfo2Dao();boolean flag = user.addUser(username,password,phone,realname);if(flag){out.println("用戶註冊成功"+"<br>");out.println("用戶名:"+username+"<br>");}else{out.println("註冊失敗");}out.println("<br>");%><%//查詢所有用戶並展現UserInfo2Dao u = new UserInfo2Dao();ResultSet rs = u.findUser();while(rs.next()){/*String unames = rs.getString("username");byte[] bb = uname.getBytes("ISO8859-1");String usernames = new String(b,"UTF-8");out.println("用戶名:"+usernames+"<br>");*/out.println(rs.getString("username")+"<br>");out.println(rs.getString("password")+"<br>");out.println(rs.getString("phone")+"<br>");out.println(rs.getString("realname")+"<br>");/* String realnames = rs.getString("realname");byte []bbb = realnames.getBytes("ISO8859-1");String rn = new String(bbb,"UTF-8");out.println("真實姓名:"+rn+"<br>");*/out.println("<br>");}%></body></html>======================================================================下面我們分這四種情況來看。A、直接在console上運行的類這種情況,運行該類首先需要JVM支持,即操作系統中必須安裝有JRE。運行過程是這樣的:首先java啟動JVM,此時JVM讀出操作系統中保存的class文件並把內容讀入內存中,此時內存中為UNICODE格式的class類,然後JVM運行它,如果此時此類需要接收用戶輸入,則類會默認用file.encoding編碼格式對用戶輸入的串進行編碼並轉化為unicode保存入內存(用戶可以設置輸入流的編碼格式)。程序運行後,產生的字元串(UNICODE編碼的)再回交給JVM,最後JRE把此字元串再轉化為file.encoding格式(用戶可以設置輸出流的編碼格式)傳遞給操作系統顯示介面並輸出到界面上。以上每一步的轉化都需要正確的編碼格式轉化,才能最終不出現亂碼現象。B、EJB類和不可以直接運行的支持類(如JavaBean類)由於EJB類和不可以直接運行的支持類,它們一般不與用戶直接交互輸入和輸出,它們常常與其它的類進行交互輸入和輸出,所以它們在第二步被編譯後,就形成了內容是UNICODE編碼的類保存在操作系統中了,以後只要它與其它的類之間的交互在參數傳遞過程中沒有丟失,則它就會正確的運行。C、JSP代碼和Servlet類經過第二步後,JSP文件也被轉化為Servlets類文件,只不過它不像標準的Servlets一校存在於classes目錄中,它存在於WEB容器的臨時目錄中,故這一步中我們也把它做為Servlets來看。對於Servlets,客戶端請求它時,WEB容器調用它的JVM來運行Servlet,首先,JVM把Servlet的class類從系統中讀出並裝入內存中,內存中是以UNICODE編碼的Servlet類的代碼,然後JVM在內存中運行該Servlet類,如果Servlet在運行的過程中,需要接受從客戶端傳來的字元如:表單輸入的值和URL中傳入的值,此時如果程序中沒有設定接受參數時採用的編碼格式,則WEB容器會默認採用ISO-8859-1編碼格式來接受傳入的值並在JVM中轉化為UNICODE格式的保存在WEB容器的內存中。Servlet運行後生成輸出,輸出的字元串是UNICODE格式的,緊接著,容器將Servlet運行產生的UNICODE格式的串(如html語法,用戶輸出的串等)直接發送到客戶端瀏覽器上並輸出給用戶,如果此時指定了發送時輸出的編碼格式,則按指定的編碼格式輸出到瀏覽器上,如果沒有指定,則默認按ISO-8859-1編碼發送到客戶的瀏覽器上。D、Java程序和資料庫之間對於幾乎所有資料庫的JDBC驅動程序,默認的在JAVA程序和資料庫之間傳遞數據都是以ISO-8859-1為默認編碼格式的,所以,我們的程序在向資料庫內存儲包含中文的數據時,JDBC首先是把程序內部的UNICODE編碼格式的數據轉化為ISO-8859-1的格式,然後傳遞到資料庫中,在資料庫保存數據時,它默認即以ISO-8859-1保存,所以,這是為什麼我們常常在資料庫中讀出的中文數據是亂碼。理解jsp文件中幾個基本的屬性概念:如下一個jsp文件:這個亂碼問題是最簡單的亂碼問題。一般新會出現。就是頁面編碼不一致導致的亂碼。<%@ page language="java" pageEncoding="UTF-8"%><%@ page contentType="text/html;charset=iso8859-1"%><html><head><title>中文問題</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body>我是個好人</body></html>注意三個地方的編碼。第一個地方的編碼(字元串轉換成位元組)格式為jsp文件的存儲格式。Eclipse會根據這個編碼格式保存文件。並編譯jsp文件成class文件,包括裡面的漢字。第二處編碼為解碼(位元組轉換成字元串)格式。因為存為UTF-8的文件被解碼為iso8859-1,這樣 如有中文肯定出亂碼。也就是必須一致。而第二處所在的這一行,可以沒有。預設也是使用iso8859-1的編碼格式。所以如果沒有這一行的話,「我是個好人」也會出現亂碼。必須一致才可以。第三處編碼為控制瀏覽器的解碼方式。如果前面的解碼都一致並且無誤的話,這個編碼格式沒有關係。有的網頁出現亂碼,就是因為瀏覽器不能確定使用哪種編碼格式。因為頁面有時候會嵌入頁面,導致瀏覽器混淆了編碼格式。出現了亂碼可見,pageEncoding和contentType都可以設置JSP源文件和響應正文中的字符集編碼.但也有區別:設置JSP源文件字符集時,優先順序為: pageEncoding > contentType.如果都沒有設置,則默認為ISO-8859-1.設置響應輸出的字符集時,優先順序為 contentType > pageEncoding.如果都沒有設置,則默認為ISO-8859-1;都設置了,以contentType為準.實際上,response.setContentType()就是根據contentType屬性設置的.總結:1. 在jsp中<%@ page contentType="text/html; charset=A" %>如果指定了,那麼在改jsp中所有構造的String(不是引用),如果沒有指定編碼,那麼這些String的編碼是A的。從request的得到的String如果沒有指定request的編碼的話,他是iso-8859-1的從別的地方得到的String是使用原來初始的編碼的,比如從資料庫得到String,如果資料庫的編碼是B,那麼該String的編碼是B而不是A的,也不是系統默認的。此時,如果要輸出的String的編碼不是A,那麼,很可能顯示亂碼的,所以首先要將String正確轉化為編碼A的String,然後輸出。2. 在jsp中<%@ page contentType="text/html; charset=A" %>沒有指定,那麼相當於指定了<%@page contentType="text/html; charset=ISO-8859-1" %>3. Servelte中如果執行了像 response.setContentType("text/html;charset=A");説明將response的字元輸出流編碼設置為A,所有要輸出的String的編碼要轉化為A的,否則會得到亂碼的。Servelet中從request得到的String的編碼和jsp中一樣的,但是在servlet java文件中構造的String是使用的系統默認的編碼的。在servelt中從外部得到的String 是使用原來的編碼的,比如從編碼為B的資料庫得到的數據是編碼為B的,不是A,也不是系統默認的編碼。=======================================================================1、pageEncoding="UTF-8"的作用是設置JSP編譯成Servlet時使用的編碼。眾所周知,JSP在服務 器上是要先被編譯成Servlet的。pageEncoding="UTF-8"的作用就是告訴JSP編譯器在將JSP文件編譯成Servlet時使用的編碼。通常,在JSP內部定義的字元串(直接在JSP中定義,而不是從瀏覽器提交的數據)出現亂碼時,很多都是由於該參數設置錯誤引起的。例如,你的 JSP文件是以GBK為編碼保存的,而在JSP中卻指定pageEncoding="UTF-8",就會引起JSP內部定義的字元串為亂碼。另外,該參數還有一個功能,就是在JSP中不指定contentType參數,也不使用response.setCharacterEncoding方法時,則充當指定對伺服器響應進行重新編碼的編碼的功能。2、contentType="text/html;charset=UTF-8"的作用是指定對伺服器響應進行重新編碼的編碼。在不使用response.setCharacterEncoding()方法時,用該參數指定對伺服器響應進行重新編碼的編碼。二者功能一樣。3、request.setCharacterEncoding("UTF-8")的作用是設置對客戶端請求進行重新編碼的編碼。該方法用來指定對瀏覽器發送來的數據進行重新編碼(或者稱為解碼)時,使用的編碼。4、response.setCharacterEncoding("UTF-8")的作用是指定對伺服器響應進行重新編碼的編碼。伺服器在將數據發送到瀏覽器前,對數據進行重新編碼時,使用的就是該編碼。其次,要說一說瀏覽器是怎麼樣對接收和發送的數據進行編碼的response.setCharacterEncoding("UTF- 8")的作用是指定對伺服器響應進行重新編碼的編碼。同時,瀏覽器也是根據這個參數來對其接收到的數據進行重新編碼(或者稱為解碼)。所以在無論你在 JSP中設置response.setCharacterEncoding("UTF-8")或者 response.setCharacterEncoding("GBK"),瀏覽器均能正確顯示中文(前提是你發送到瀏覽器的數據編碼是正確的,比如正 確設置了pageEncoding參數等)。讀者可以做個實驗,在JSP中設置response.setCharacterEncoding("UTF- 8"),在IE中顯示該頁面時,在IE的菜單中選擇"查看(V)"à"編碼(D)"中可以查看到是" Unicode(UTF-8)",而在在JSP中設置response.setCharacterEncoding("GBK"),在IE中顯示該頁面 時,在IE的菜單中選擇"查看(V)"à"編碼(D)"中可以查看到是"簡體中文(GB2312)"。瀏覽器在發送數據時,對URL和參數會 進行URL編碼,對參數中的中文,瀏覽器也是使response.setCharacterEncoding參數來進行URL編碼的。以百度和 GOOGLE為例,如果你在百度中搜索"漢字",百度會將其編碼為"%BA%BA%D7%D6"。而在GOOGLE中搜索"漢字",GOOGLE會將其編 碼為"%E6%B1%89%E5%AD%97",這是因為百度的response.setCharacterEncoding參數為GBK,而 GOOGLE的的response.setCharacterEncoding參數為UTF-8。瀏覽器在接收伺服器數據和發送數據到伺服器 時所使用的編碼是相同的,默認情況下均為JSP頁面的response.setCharacterEncoding參數(或者contentType和 pageEncoding參數),我們稱其為瀏覽器編碼。當然,在IE中可以修改瀏覽器編碼(在IE的菜單中選擇"查看(V)"à"編碼(D)"中修 改),但通常情況下,修改該參數會使原本正確的頁面中出現亂碼。一個有趣的例子是,在IE中瀏覽GOOGLE的主頁時,將瀏覽器編碼修改為"簡體中文 (GB2312)",此時,頁面上的中文會變成亂碼,不理它,在文本框中輸入"漢字",提交,GOOGLE會將其編碼為"%BA%BA%D7%D6",可 見,瀏覽器在對中文進行URL編碼時,使用的就是瀏覽器編碼。弄清了瀏覽器是在接收和發送數據時,是如何對數據進行編碼的了,我們再來看看伺服器是在接收和發送數據時,是如何對數據進行編碼的。對於發送數據,伺服器按照response.setCharacterEncoding>contentType>pageEncoding的優先順序,對要發送的數據進行編碼。對於接收數據,要分三種情況。一種是瀏覽器直接用URL提交的數據,另外兩種是用表單的GET和POST方式提交的數據。因為各種WEB伺服器對這三種方式的處理也不相同,所以我們以Tomcat5.0為例。無論使用那種方式提交,如果參數中包含中文,瀏覽器都會使用當前瀏覽器編碼對其進行URL編碼。對於表單中POST方式提交的數據,只要在接收數據的JSP中正確request.setCharacterEncoding參數,即將對客戶端請求進行重 新編碼的編碼設置成瀏覽器編碼,就可以保證得到的參數編碼正確。有寫讀者可能會問,那如何得到瀏覽器編碼呢?上面我們提過了,在默認請情況下,瀏覽器編碼 就是你在響應該請求的JSP頁面中response.setCharacterEncoding設置的值。所以對於POST表單提交的數據,在獲得數據的 JSP頁面中request.setCharacterEncoding要和生成提交該表單的JSP頁面的 response.setCharacterEncoding設置成相同的值。對於URL提交的數據和表單中GET方式提交的數據,在接收數 據的JSP中設置request.setCharacterEncoding參數是不行的,因為在Tomcat5.0中,默認情況下使用ISO- 8859-1對URL提交的數據和表單中GET方式提交的數據進行重新編碼(解碼),而不使用該參數對URL提交的數據和表單中GET方式提交的數據進行 重新編碼(解碼)。要解決該問題,應該在Tomcat的配置文件的Connector標籤中設置useBodyEncodingForURI或者 URIEncoding屬性,其中useBodyEncodingForURI參數表示是否用request.setCharacterEncoding 參數對URL提交的數據和表單中GET方式提交的數據進行重新編碼,在默認情況下,該參數為false(Tomcat4.0中該參數默認為 true);URIEncoding參數指定對所有GET方式請求(包括URL提交的數據和表單中GET方式提交的數據)進行統一的重新編碼(解碼)的編 碼。URIEncoding和useBodyEncodingForURI區別是,URIEncoding是對所有GET方式的請求的數據進行統一的重新 編碼(解碼),而useBodyEncodingForURI則是根據響應該請求的頁面的request.setCharacterEncoding參數 對數據進行的重新編碼(解碼),不同的頁面可以有不同的重新編碼(解碼)的編碼。所以對於URL提交的數據和表單中GET方式提交的數據,可以修改 URIEncoding參數為瀏覽器編碼或者修改useBodyEncodingForURI為true,並且在獲得數據的JSP頁面中 request.setCharacterEncoding參數設置成瀏覽器編碼。
推薦閱讀:
推薦閱讀:
※黃聰:解決python中文處理亂碼,先要弄懂「字元」和「位元組」的差別
※頁面跳轉亂碼解決之道
※毫無意義的意義~
※瀏覽器亂碼