整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Java Web開發中文亂碼總結-先收藏了慢慢看

          開發過程中,經常遇到中文亂碼問題,以前總是解決就好,并沒有對該問題總結一下,現在來總結一下開發過程中常見的中文亂碼問題。

          一、有必要了解一些基本的編碼知識:

          • 這篇字符編碼筆記是必讀的

          • jsp的三次編碼

            第一階段:JVM將.jsp文件編譯為.java文件。JVM先讀取pageEncoding的值,根據該值去讀取.jsp文件,然后由指定的編碼方案生成UTF-8的.java文件。

            第二階段:JVM將.java文件轉換為.class文件,從UTF-8至UTF-8。這個過程就與任何編碼的設置都沒有關系了,經過這個階段后.java文件就轉換成了統一的UTF-8編碼的.class文件了。

            第三階段:服務器將處理的結果返回給瀏覽器,這個階段則依靠contentType的charset,如果設置了charset則瀏覽器就會使用指定的編碼格式進行解碼,否則采用默認的ISO-8859-1編碼格式進行解碼處理。

          • jsp中的編碼設置

          1. pageEncoding:<%@ page pageEncoding=”UTF-8”%>

            上文中第一階段,使用該值去讀取jsp文件,為避免中文亂碼,跟jsp文件編碼一致;對服務器響應進行重新編碼,即jsp的輸出流在瀏覽器中顯示的編碼(不是主要作用)。

          2. contentType: <%@ page contentType=”text/html;charset=UTF-8”%>

            使用該值對服務器響應進行重新編碼,即jsp的輸出流在瀏覽器中顯示的編碼;對表單get和post請求數據編碼;上文中第一階段,使用該值去讀取jsp文件(不是主要作用)。

          3. < META http-equiv=”Content-Type” content=”text/html;charset=UTF-8”>

            網頁的編碼信息 ,說明頁面制作所使用的編碼。

          4. request.setCharacterEncoding()

            可用在servlet和jsp頁面中,作用是設置對客戶端請求進行重新編碼的編碼,即post方式提交的數據進行編碼。

          5. response.setCharacterEncoding()

            與<%@ page contentType=”text/html;charset=UTF-8”%>一樣。

          6. response.setContentType()

            與<%@ page contentType=”text/html;charset=UTF-8”%>一樣。

          7. response.setHeader(“Content-Type”,”text/html;charset=UTF-8”)

            與< META http-equiv=”Content-Type” content=”text/html; charset=UTF-8”>一樣。

            注意:上文1,2,3中有部分功能是一樣的,是有優先級的,在讀取jsp文件時,1>2;在對服務器響應進行編碼的時候,2>1>3,一般情況下,1,2都寫。

        1. http請求默認以”ISO-8859-1”的編碼來傳送URL的。

        2. 二、中文亂碼的幾種情況及最簡單的解決方案:

          • pageEncoding設置錯誤

            pageEncoding設置為jsp文件的編碼類型。

          • 查詢字符串包含中文

            中文的編碼方式取決于瀏覽器,chrome為UTF-8,IE為GB2312,這是由于瀏覽器并沒有遵循URI編碼規范。有兩種解決方法:

          1. 開發過程中,將查詢字符串提前編碼,

            如:http://www.baidu.com/demo?demo=%D6%D0%B9%FA (UTF-8編碼)

          2. 在Servlet的doGet()方法中添加

          1
          String value = new String(request.getParameter("parameterName").getBytes("ISO-8859-1"),"瀏覽器的編碼方式");
          • 表單中的get和post數據包含中文

            中文的編碼方式取決于上文的contentType中的charset,有兩種解決辦法:

          1. 在Servlet的doPost()方法中添加request.setCharacterEncoding(“charset的值”);(僅對post有用)

          2. 在Servlet的doPost()方法中添加

          1
          String value = new String(request.getParameter("parameterName").getBytes("ISO-8859-1"),"charset的值");

          三、原理

          我們通過上面的方法可以解決亂碼問題,下面講講原理:

          • 客戶端發到服務器的數據需要在客戶端進行編碼,類似于:String parameterName = "中國".getBytes("UTF-8")然后將編碼后的數據發到服務器。

          • 客戶端接受數據,request.getParameter(“”)的作用就是對接收到的數據進行解碼,默認使用ISO-8859-1進行解碼,可以使用request.setCharacterEncoding(“”)進行設置,但僅對post有用。假如我們使用默認的ISO-8859-1,肯定亂碼,因為編碼跟解碼不一致,那此時怎么辦呢,引出了上文中的兩種解決方案:使用request.setCharacterEncoding(“”)改變request.getParameter(“”)的解碼方式或者new String(request.getParameter("parameterName").getBytes("ISO-8859-1"),"charset的值")將request.getParameter(“”)解碼的數據重新編碼再解碼。

          四、其他

          在jsp中的頁面使用response.setContentType()等設置字符集會破壞jsp容器自身的頁面編碼,會引起html中字符亂碼,腳本不會亂,所以不建議設置。在開發中多采用page指令設置字符集。

          1234567891011121314151617
          <%	response.setContentType("text/html;charset=UTF-8");	String str = new String("你好".getBytes("iso-8859-1"),"utf-8");%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><p>你好</p><br><%=str %></body></html>

          網頁測試

          開發一個完整的web項目時,總是會遇到各種各樣的中文亂碼問題,例如頁面顯示亂碼,表單提交亂碼,數據庫存儲亂碼等等,雖然目前也能找到各種各樣的解決方案,但是大部分都沒有總結全面。這里我就根據自己以前的總結以及查找的一些資料進行整理,寫出這篇文章。

          (1)準備知識

          凡是會出現亂碼的地方都是由于編碼的問題產生的,頁面默認編碼為ISO-8859-1,簡體中文編碼為GB2312,中文漢字集(簡體與繁體)編碼為GBK,國際編碼為UTF-8。由于UTF-8編碼支持的語言類型最廣,所以在這里建議凡是用到字符編碼的地方都使用UTF-8格式。接下來看看所有可能出現亂碼場景的解決方案。

          (2)頁面顯示亂碼

          • html頁面

          在頁面的<head>標簽內添加<meta>標簽,內容如下

          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

          或者

          <meta charset="utf-8">

          • jsp頁面

          在jsp頁面的頂部加上以下代碼,重點是charset=UTF-8和pageEncoding="UTF-8"

          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

          同時在<head>標簽下添加<meta>標簽,代碼如下

          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

          為了讓所有新建的jsp頁面默認為utf-8編碼,可以在相應的IDE下設置。以eclipse為例,window > Prefrences > Web > JSP Files > Encoding處改成支持UTF-8格式的選項。

          jsp頁面編碼設置

          (3)客戶端和服務器端傳輸亂碼

          • tomcat配置

          在tomcat安裝目錄下 > conf > server.xml,在server.xml文件中找到Connector port="8080"所在的標簽,在標簽內部添加URIEncoding="utf-8",添加后為

          <Connector port="8080"

          protocol="HTTP/1.1"

          maxThreads="150"

          connectionTimeout="200000"

          redirecPort="8443"

          URIEncoding="utf-8"/>

          • Request請求

          例如以下請求方式

          <a href="/myProject/displayServlet?username=張三&password=123">顯示用戶名和密碼</a>

          這種情況下,可以看出來該請求是GET請求,在接收請求時如果出現亂碼,需要使用以下代碼

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

          在表單提交的時候,如果是POST請求,與GET請求不一樣,添加的是以下代碼

          request.setCharacterEncoding("utf-8")

          注意:request.setCharacterEncoding("utf-8")只在POST請求下生效

          • Response響應

          在servlet中利用response進行輸出時,如果出現亂碼,需要添加以下代碼

          response.setContentType("text/html;charset=utf-8");

          response.setCharacterEncoding("UTF-8")

          • struts.xml

          如果使用了Struts2框架,在strust.xml文件中添加如下代碼

          <constant name="struts.i18n.encoding" value="UTF-8"></constant>

          • Filter

          還有一種最保險的方法是添加自定義編碼過濾器,或者使用spring自帶的過濾器,在web.xml中添加以下代碼

          <filter>

          <filter-name>encodingFilter</filter-name>

          <filter-class>

          org.springframework.web.filter.CharacterEncodingFilter

          </filter-class>

          <init-param>

          <param-name>encoding</param-name>

          <param-value>UTF-8</param-value>

          </init-param>

          </filter>

          (4)數據庫插入亂碼

          在服務器獲取到正確數據后,插入到數據庫卻出現亂碼,很有可能是數據庫的編碼問題,按照以下方法來做可以避免這種問題

          • 安裝

          在數據庫安裝過程中會選擇編碼方式,此時選擇utf-8格式

          • 創建數據庫

          在創建數據庫的過程中,按照以下例子

          CREATE DATABASE `share` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

          • 創建表

          在創建表生成的SQL后面加上

          ENGINE=InnoDB DEFAULT CHARSET=utf8;

          • 數據庫連接

          在使用hibernate或者spring連接數據庫時,按照以下例子

          <property name="connection.url">

          <![CDATA[jdbc:mysql://localhost:3306/testDB?useUnicode=true&characterEncoding=utf8]]>

          </property>

          (5)總結

          在完成上述的講解后,基本不會出現亂碼的情形,大家也可以嘗試下。如果出現了亂碼的情況,也不要著急,先冷靜分析,亂碼是出現在哪一個環節,然后按照我講的幾部分去找對應的解決辦法。

          同時也是教大家一種問問題的方式,先自己分析問題出在哪里,直接問別人“我的項目中出現了中文亂碼,你能幫我看一下么?”和“數據返回頁面的時候出現亂碼了,你能幫我看一下么?”你覺得別人會喜歡哪種提問方式呢?

          者 | 丁彥軍

          責編 | 仲培藝

          近日,有位粉絲向我請教,在爬取某網站時,網頁的源代碼出現了中文亂碼問題,本文就將與大家一起總結下關于網絡爬蟲的亂碼處理。注意,這里不僅是中文亂碼,還包括一些如日文、韓文 、俄文、藏文之類的亂碼處理,因為他們的解決方式是一致的,故在此統一說明。

          亂碼問題的出現

          就以爬取 51job 網站舉例,講講為何會出現“亂碼”問題,如何解決它以及其背后的機制。

          代碼示例:

          import requests
          url = "http://search.51job.com"
          res = requests.get(url)
          print(res.text)
          

          顯示結果:

          打印 res.text 時,發現了什么?中文亂碼?。。〔贿^發現,網頁的字符集類型采用的是 GBK 編碼格式。

          我們知道 Requests 會基于 HTTP 頭部對響應的編碼作出有根據的推測。當你訪問 r.text 之時,Requests 會使用其推測的文本編碼。你可以找出 Requests 使用了什么編碼,并且能夠使用 r.encoding 屬性來改變它。

          接下來,我們一起通過 Resquests 的一些用法,來看看 Requests 會基于 HTTP 頭部對響應的編碼方式。

          print(res.encoding) #查看網頁返回的字符集類型
          print(res.apparent_encoding) #自動判斷字符集類型
          

          輸出結果為:

          可以發現 Requests 推測的文本編碼(也就是網頁返回即爬取下來后的編碼轉換)與源網頁編碼不一致,由此可知其正是導致亂碼原因。

          亂碼背后的奧秘

          當源網頁編碼和爬取下來后的編碼轉換不一致時,如源網頁為 GBK 編碼的字節流,而我們抓取下后程序直接使用 UTF-8 進行編碼并輸出到存儲文件中,這必然會引起亂碼,即當源網頁編碼和抓取下來后程序直接使用處理編碼一致時,則不會出現亂碼,此時再進行統一的字符編碼也就不會出現亂碼了。最終爬取的所有網頁無論何種編碼格式,都轉化為 UTF-8 格式進行存儲。

          注意:區分源網編碼 A-GBK、程序直接使用的編碼 B-ISO-8859-1、統一轉換字符的編碼 C-UTF-8。

          在此,我們拓展講講 Unicode、ISO-8859-1、GBK2312、GBK、UTF-8 等之間的區別聯系,大概如下:

          最早的編碼是 ISO8859-1,和 ASCII 編碼相似。但為了方便表示各種各樣的語言,逐漸出現了很多標準編碼。ISO8859-1 屬于單字節編碼,最多能表示的字符范圍是 0-255,應用于英文系列。很明顯,ISO8859-1 編碼表示的字符范圍很窄,無法表示中文字符。

          1981 年中國人民通過對 ASCII 編碼的中文擴充改造,產生了 GB2312 編碼,可以表示 6000 多個常用漢字。但漢字實在是太多了,包括繁體和各種字符,于是產生了 GBK 編碼,它包括了 GB2312 中的編碼,同時擴充了很多。中國又是個多民族國家,各個民族幾乎都有自己獨立的語言系統,為了表示那些字符,繼續把 GBK 編碼擴充為 GB18030 編碼。每個國家都像中國一樣,把自己的語言編碼,于是出現了各種各樣的編碼,如果你不安裝相應的編碼,就無法解釋相應編碼想表達的內容。終于,有個叫 ISO 的組織看不下去了。他們一起創造了一種編碼 Unicode,這種編碼非常大,大到可以容納世界上任何一個文字和標志。所以只要電腦上有 Unicode 這種編碼系統,無論是全球哪種文字,只需要保存文件的時候,保存成 Unicode 編碼就可以被其他電腦正常解釋。Unicode 在網絡傳輸中,出現了兩個標準 UTF-8 和 UTF-16,分別每次傳輸 8 個位和 16 個位。于是就會有人產生疑問,UTF-8 既然能保存那么多文字、符號,為什么國內還有這么多使用 GBK 等編碼的人?因為 UTF-8 等編碼體積比較大,占電腦空間比較多,如果面向的使用人群絕大部分都是中國人,用 GBK 等編碼也可以。

          也可以這樣來理解:字符串是由字符構成,字符在計算機硬件中通過二進制形式存儲,這種二進制形式就是編碼。如果直接使用 “字符串??字符??二進制表示(編碼)” ,會增加不同類型編碼之間轉換的復雜性。所以引入了一個抽象層,“字符串??字符??與存儲無關的表示??二進制表示(編碼)” ,這樣,可以用一種與存儲無關的形式表示字符,不同的編碼之間轉換時可以先轉換到這個抽象層,然后再轉換為其他編碼形式。在這里,Unicode 就是 “與存儲無關的表示”,UTF-8 就是 “二進制表示”。

          亂碼的解決方法

          根據原因來找解決方法,就非常簡單了。

          方法一:直接指定 res.encoding

          import requests
          url = "http://search.51job.com"
          res = requests.get(url)
          res.encoding = "gbk"
          html = res.text
          print(html)
          

          方法二:通過 res.apparent_encoding 屬性指定

          import requests
          url = "http://search.51job.com"
          res = requests.get(url)
          res.encoding = res.apparent_encoding
          html = res.text
          print(html)
          

          方法三:通過編碼、解碼的方式

          import requests
          url = "http://search.51job.com"
          res = requests.get(url)
          html = res.text.encode('iso-8859-1').decode('gbk')
          print(html)
          

          輸出結果:

          基本思路三步走:確定源網頁的編碼 A---GBK、程序通過編碼 B---ISO-8859-1 對源網頁數據還原、統一轉換字符的編碼 C-UTF-8。至于為啥出現統一轉碼這一步呢? 網絡爬蟲系統數據來源很多,不可能使用數據時,再轉化為其原始的數據,這樣做是很廢事的。所以一般的爬蟲系統都要對抓取下來的結果進行統一編碼,從而在使用時做到一致對外,方便使用。

          比如如果我們想講網頁數據保存下來,則會將起轉為 UTF-8,代碼如下:

          with open("a.txt",'w',encoding='utf-8') as f:
           f.write(html)
          

          總結

          關于網絡爬蟲亂碼問題,這里不僅給出了一個解決方案,還深入到其中的原理,由此問題引申出很多有意思的問題,如 UTF-8、GBK、GB2312 的編碼方式怎樣的?為什么這樣轉化就可以解決問題?

          最后,多動腦,多思考,多總結,致每一位碼農!

          本文為作者投稿,版權歸其所有。


          主站蜘蛛池模板: 3d动漫精品啪啪一区二区免费| 人妻体内射精一区二区三四| 午夜视频在线观看一区二区| 日韩爆乳一区二区无码| 杨幂AV污网站在线一区二区| 亚洲av无码一区二区三区四区 | 一区二区三区免费在线观看| 国产精品一区二区四区| 亚洲AV香蕉一区区二区三区 | 亚洲国产福利精品一区二区| 亚洲国产精品一区第二页| 狠狠色婷婷久久一区二区| 国产福利视频一区二区| 国产亚洲福利精品一区| 久久人妻内射无码一区三区| 乱码精品一区二区三区| 国产成人久久精品区一区二区 | 一区二区在线免费视频| 成人午夜视频精品一区| 国产一区在线视频| 国产成人精品视频一区二区不卡| 国产午夜福利精品一区二区三区 | 正在播放国产一区| 丝袜美腿高跟呻吟高潮一区| 中文字幕一区二区三区四区| 中文字幕人妻无码一区二区三区 | 国产精品99精品一区二区三区 | 亚洲av不卡一区二区三区| 色窝窝无码一区二区三区成人网站| 搡老熟女老女人一区二区| 日韩毛片基地一区二区三区| 少妇人妻精品一区二区| 亚洲高清偷拍一区二区三区| 夜夜添无码试看一区二区三区| 久久青草精品一区二区三区| 亚洲AV无码一区二区乱子仑| 国产一区二区三区免费在线观看| 国产精品无码不卡一区二区三区 | 嫩B人妻精品一区二区三区| 亚洲国产精品自在线一区二区| 无码国产精品一区二区免费|