1. 程式人生 > >spark 中文編碼處理

spark 中文編碼處理

日誌的格式是GBK編碼的,而hadoop上的編碼是用UTF-8寫死的,導致最終輸出亂碼。

研究了下Java的編碼問題。

網上其實對spark輸入檔案是GBK編碼有現成的解決方案,具體程式碼如下

複製程式碼
import org.apache.hadoop.io.LongWritable
import org.apache.hadoop.io.Text
import org.apache.hadoop.mapred.TextInputFormat

rdd = ctx.hadoopFile(file_list, classOf[TextInputFormat],
            classOf[LongWritable], classOf[Text]).map(
            pair 
=> new String(pair._2.getBytes, 0, pair._2.getLength, "GBK"))
複製程式碼

這種想法的來源是基於

複製程式碼
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); }
複製程式碼

但這種方法還有一個問題,

大家都知道gbk是2~3個位元組編碼的。如果日誌中按照直接截斷,導致按照gbk讀取檔案的時候,將後面的分隔符\t一併讀取了 ,導致按照\t split的時候,欄位的個數不對(或者說順序錯位了)。

這個時候,需要找到一種單位元組的解析方案,即 ISO-8859-1編碼。程式碼如下

rdd = ctx.hadoopFile(file_list, classOf[TextInputFormat],
            classOf[LongWritable], classOf[Text]).map(
            pair 
=> new String(pair._2.getBytes, 0, pair._2.getLength, "ISO-8859-1"))

但這又帶來了一個問題,即輸出的結果(按照UTF-8儲存)是亂碼,不可用。

如果我們換一種思路來考慮這個問題,Java或scala中如何將一個gbk檔案轉換為UTF8?網上有很多的現成的程式碼,具體到我們的場景,以行為單位處理的話,示例程式碼如下

複製程式碼
public class Encoding {
    private static String kISOEncoding = "ISO-8859-1";
    private static String kGBKEncoding = "GBK";
    private static String kUTF8Encoding = "UTF-8";
    
    public static void main(String[] args) throws UnsupportedEncodingException {
        try {
            File out_file = new File(args[1]);
            Writer out = new BufferedWriter(new OutputStreamWriter(
                         new FileOutputStream(out_file), kUTF8Encoding));
            List<String> lines = Files.readAllLines(Paths.get(args[0]), Charset.forName(kGBKEncoding));
            for (String line : lines) {
                out.append(line).append("\n");
            }
            out.flush();
            out.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}
複製程式碼

如上的程式碼給了我們一個啟示,即在寫入檔案的時候,系統自動進行了編碼的轉換,我們沒必要對進行單獨的直接轉換處理。

通過查詢資料,Java中字元編碼是內部編碼,即位元組流按照編碼轉化為String。

所謂結合以上兩點認識,我們模擬在spark上以ISO-8859-1

開啟檔案和以UTF-8寫入檔案的過程,發現只需要將其強制轉換為GBK的string即可,最終得到的檔案以UTF-8開啟不是亂碼,具體程式碼如下。

複製程式碼
public class Encoding {
    private static String kISOEncoding = "ISO-8859-1";
    private static String kGBKEncoding = "GBK";
    private static String kUTF8Encoding = "UTF-8";
    
    public static void main(String[] args) throws UnsupportedEncodingException {
        try {
            File out_file = new File(args[1]);
            Writer out = new BufferedWriter(new OutputStreamWriter(
                         new FileOutputStream(out_file), kUTF8Encoding));
            List<String> lines = Files.readAllLines(Paths.get(args[0]), Charset.forName(kISOEncoding));
            for (String line : lines) {
                String gbk_str = new String(line.getBytes(kISOEncoding), kGBKEncoding);
                out.append(gbk_str).append("\n");
            }
            out.flush();
            out.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}
複製程式碼

完美的解決了。。。花費了一個工作日解決才解決的問題,對Java還是不夠熟練啊。

總結出來,希望對大家有用。

總結

1. 要舉一反三

2. 學會google,最近我就指望著它活著了。