Hadoop 中文编码相关问题 -- mapreduce程序处理GBK编码数据并输出GBK编码数据

原创 2013年09月19日 13:39:32

输入是GBK文件, 输出也是 GBK 文件的示例代码:

Hadoop处理GBK文本时,发现输出出现了乱码,原来HADOOP在涉及编码时都是写死的UTF-8,如果文件编码格式是其它类型(如GBK),则会出现乱码。

此时只需在mapper或reducer程序中读取Text时,使用transformTextToUTF8(text, "GBK");进行一下转码,以确保都是以UTF-8的编码方式在运行。

  
  1. public static Text transformTextToUTF8(Text text, String encoding) {
  2. String value = null;
  3. try {
  4. value = new String(text.getBytes(), 0, text.getLength(), encoding);
  5. } catch (UnsupportedEncodingException e) {
  6. e.printStackTrace();
  7. }
  8. return new Text(value);
  9. }

这里核心代码是: 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格式输出中文。


详见我的有道笔记: http://note.youdao.com/share/?id=04678102d70bae8334b26df2c9c4a961&type=note



hadoop解决中文输出乱码

hadoop涉及输出文本的默认输出编码统一用没有BOM的UTF-8的形式,但是对于中文的输出window系统默认的是GBK,有些格式文件例如CSV格式的文件用excel打开输出编码为没有BOM的UTF...
  • Duke147
  • Duke147
  • 2013年12月05日 14:25
  • 2800

hadoop读取hdfs文件中的中文乱码解决办法

FileSystem fs = FileSystem.get(conf); Path file = new Path("hdfs://localhost:9000/wordcount/data/wo...

MapReduce程序处理hdfs中数据乱码问题

最近在写MapReduce程序,实现Writable接口时发现总是读不进中文字,读出来全是乱码,找了一晚上发现问题,原来hadoop只支持UTF-8的字符集,因此,在存储Text变量时要先把strin...
  • qzlzwhx
  • qzlzwhx
  • 2014年07月28日 16:06
  • 1523

mapreduce排序和二次排序以及全排序

自己学习排序和二次排序的知识整理如下。 1.Hadoop的序列化格式介绍:Writable 2.Hadoop的key排序逻辑 3.全排序 4.如何自定义自己的Writable类型 5.如何实...

MapReducer中文编码

如果输入文件有中文,输出文件可能会出现乱码。乱码问题的话一般都是编解码错误。 本文的最后有一篇参考文档,那篇文档已经解决了问题了,但是,可是自己是小白,那篇文档没做解释的话,有的我理解不了,所以就把...

MapReduce任务中文部分正常,部分乱码

集群上提交的mr任务,发现结果中有的中文正常,有的中文是论码。 分析了一下,应该是集群中hadoop节点的编码配置不一样。可以加上下面的参数: mapred.child.env="LANG=en_...
  • wisgood
  • wisgood
  • 2017年02月20日 10:29
  • 502

mapreduce 输出乱码

Hadoop处理GBK文本时,发现输出出现了乱码,原来HADOOP在涉及编码时都是写死的UTF-8,如果文件编码格式是其它类型(如GBK),则会出现乱码。 此时只需在mapper或reducer...

Hadoop字符集转换问题

hadoop中输入文件是GBK,这个是后需要进行转换。 这个教程就是进行转换的办法,实际使用OK。...

Hive使用GBK等非UTF8字符集

Hive使用GBK等非UTF8字符集

上传到HDFS上的文件遇到乱码问题

今天写了一个小MapReduce的程序,跑在集群上一切正常,但是当我查看运行结果时却发现了bug,结果中的中文文字竟然是乱码;我就开始了我的找错之路。。。。 首先看看HDFS上我要用的文件本身内容有没...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Hadoop 中文编码相关问题 -- mapreduce程序处理GBK编码数据并输出GBK编码数据
举报原因:
原因补充:

(最多只允许输入30个字)