Hadoop 中文編碼相關問題 -- mapreduce程式處理GBK編碼數據並輸出GBK編碼數據 Hadoop 中文編碼相關問題 -- mapreduce程式處理GBK編碼數據並輸出GBK編碼數據 Hadoop 中文編碼相關問題 -- mapreduce程式處理GBK編碼數據並輸出GBK編碼數據 ...
Hadoop 中文編碼相關問題 -- mapreduce程式處理GBK編碼數據並輸出GBK編碼數據
輸入是GBK文件, 輸出也是 GBK 文件的示例代碼:
Hadoop處理GBK文本時,發現輸出出現了亂碼,原來HADOOP在涉及編碼時都是寫死的UTF-8,如果文件編碼格式是其它類型(如GBK),則會出現亂碼。
此時只需在mapper或reducer程式中讀取Text時,使用transformTextToUTF8(text, "GBK");進行一下轉碼,以確保都是以UTF-8的編碼方式在運行。
- public static Text transformTextToUTF8(Text text, String encoding) {
- String value = null;
- try {
- value = new String(text.getBytes(), 0, text.getLength(), encoding);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- return new Text(value);
- }
這裡核心代碼是: String line=new String(text.getBytes(),0,text.getLength(),"GBK"); //這裡的value是Text類型
若直接使用 String line=value.toString(); 會輸出亂碼, 這是由Text這個Writable類型造成的。初學時,一直認為和LongWritable對long的封裝一樣,Text類型是String的Writable封裝。但其實Text和String還是有些區別,它是一種UTF-8格式的Writable,而Java中的String是Unicode字元。所以直接使用value.toString()方法,會預設其中的字元都是UTF-8編碼過的,因而原本GBK編碼的數據使用Text讀入後直接使用該方法就會變成亂碼。
正確的方法是將輸入的Text類型的value轉換為位元組數組(value.getBytes()),使用String的構造器String(byte[] bytes, int offset, int length, Charset charset),通過使用指定的charset解碼指定的byte子數組,構造一個新的String。
如果需要map/reduce輸出其它編碼格式的數據,需要自己實現OutputFormat,在其中指定編碼方式,而不能使用預設的TextOutputFormat。
具體的範例可以見淘寶數據平臺與產品部官方博客上的博文 http://www.tbdata.org/archives/244 。
來自: Hadoop的map/reduce作業輸入非UTF-8編碼數據的處理原理
以下摘自 淘寶數據平臺與產品部官方博客:
1 中文問題 從url中解析出中文,但hadoop中列印出來仍是亂碼?我們曾經以為hadoop是不支持中文的,後來經過查看源代碼,發現hadoop僅僅是不支持以gbk格式輸出中文而己。
這是TextOutputFormat.class中的代碼,hadoop預設的輸出都是繼承自FileOutputFormat來的,FileOutputFormat的兩個子類一個是基於二進位流的輸出,一個就是基於文本的輸出TextOutputFormat。
public static class TextOutputFormat<K, V> extends FileOutputFormat<K, V> { protected static class LineRecordWriter<K, V> implements RecordWriter<K, V> { private static final String utf8 = “UTF-8″;//這裡被寫死成了utf-8 private static final byte[] newline; static { try { newline = “\n”.getBytes(utf8); } catch (UnsupportedEncodingException uee) { throw new IllegalArgumentException(“can’t find ” + utf8 + ” encoding”); } } … public LineRecordWriter(DataOutputStream out, String keyValueSeparator) { this.out = out; try { this.keyValueSeparator = keyValueSeparator.getBytes(utf8); } catch (UnsupportedEncodingException uee) { throw new IllegalArgumentException(“can’t find ” + utf8 + ” encoding”); } } … private void writeObject(Object o) throws IOException { if (o instanceof Text) { Text to = (Text) o; out.write(to.getBytes(), 0, to.getLength());//這裡也需要修改 } else { out.write(o.toString().getBytes(utf8)); } } … } 可以看出hadoop預設的輸出寫死為utf-8,因此如果decode中文正確,那麼將Linux客戶端的character設為utf-8是可以看到中文的。因為hadoop用utf-8的格式輸出了中文。 因為大多數資料庫是用gbk來定義欄位的,如果想讓hadoop用gbk格式輸出中文以相容資料庫怎麼辦? 我們可以定義一個新的類: public class GbkOutputFormat<K, V> extends FileOutputFormat<K, V> { protected static class LineRecordWriter<K, V> implements RecordWriter<K, V> { //寫成gbk即可 private static final String gbk = “gbk”; private static final byte[] newline; static { try { newline = “\n”.getBytes(gbk); } catch (UnsupportedEncodingException uee) { throw new IllegalArgumentException(“can’t find ” + gbk + ” encoding”); } } … public LineRecordWriter(DataOutputStream out, String keyValueSeparator) { this.out = out; try { this.keyValueSeparator = keyValueSeparator.getBytes(gbk); } catch (UnsupportedEncodingException uee) { throw new IllegalArgumentException(“can’t find ” + gbk + ” encoding”); } } … private void writeObject(Object o) throws IOException { if (o instanceof Text) { // Text to = (Text) o; // out.write(to.getBytes(), 0, to.getLength()); // } else {
out.write(o.toString().getBytes(gbk)); } } … } 然後在mapreduce代碼中加入conf1.setOutputFormat(GbkOutputFormat.class) 即可以gbk格式輸出中文。
|