歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Spark 處理中文亂碼問題(UTF-8編碼)

Spark 處理中文亂碼問題(UTF-8編碼)

日期:2017/2/28 13:50:04   编辑:Linux教程

問題場景

要用spark處理一大堆微信日志數據,日志存放在HDFS上,是xml格式,裡面有大量的中文。用scala + java實現了xml的處理邏輯,其中有一步是要獲取xml中的一個title字段,中文。不管怎麼抓取,最終得到的中文都會變成一堆“?????”,亂碼了。從xml中獲取非中文字段,沒有任何問題。也就是說,代碼的邏輯是沒什麼問題的。

問題解析

直接用Hadoop fs -text或者hadoop fs -cat查看HDFS上的文件,是可以正常顯示的,也就是說HDFS上存放的原數據是好的。那麼就肯定是讀取數據或者處理數據的過程中出了問題。spark on yarn的數據處理,同時涉及了HDFS,App driver, App excutor之間的交互,所以還真沒法一下就判斷出是哪一步傳輸中出了問題。抽絲剝繭,先梳理一遍spark的處理邏輯:

(1) 從HDFS把xml讀取到每個NM上的executor中(spark on yarn環境)

(2) 在executor中對xml進行處理,獲取中文字段。這裡我實現了一個java方法,調用dom來解析xml。

(3) 把解析後的字段collect到driver中,做存儲或者輸出打印等。

(4) 或者把解析後的字段重新存入HDFS

進入Spark-shell,依次驗證這幾個步驟。讀入HDFS上的xml文件,然後直接寫入HDFS,檢查發現字符顯示正常,排除步驟(1)(4)。讀入HDFS上的xml文件,collect到driver中,然後println,字符顯示正常,排除步驟(3)。說明問題出在executor對字段的解析處理過程中。

無論漢字還是英文字符,本質上還是一組字節流,所以出現亂碼,只能是編碼解析出了問題。查看發現,代碼中只有一個地方對xml文件中的字符做了解析,就是這裡:

		DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
		InputStream strm =  new ByteArrayInputStream(xmlStream.getBytes());
		Document doc = dbBuilder.parse(strm);

把string轉為inputStream的過程。 找到了出問題的位置,下一步就是檢測。

登錄到executor所在的hadoop節點,進入spark-shell, 輸入System.getProperty("file.encoding"),返回”ISO-8859-1“,說明它的默認編碼方式是ISO-8859-1。另一種檢測方法,是定義一個String變量等於一個漢字,然後a.getBytes().length。檢查它的字節數,並推斷對應的字符編碼。UTF8漢字占3個字節,GBK漢字占2個字節。

ISO-8895-1占1字節,用ISO-8895-1的方式把漢字轉成字節流,然後轉回的過程中,肯定會損失一部分數據,所以會亂碼。

問題定位到後,解決就很簡單了。 在所有涉及到字節轉換時,一定要指定編碼方式。類似這樣:

String -> Byte:

string.getBytes("UTF-8")

Byte -> String:

new String(bytes, "UTF-8")

Copyright © Linux教程網 All Rights Reserved