WordCount on(Key,Value)输出Map Reduce

时间:2013-08-01 19:35:40

标签: java hadoop mapreduce word-count

我使用Java在Hadoop中使用MapReduce应用程序的输出获得了几个(title , text )有序对。

现在我想在这些有序对的文本字段上实现字数统计。

所以我的最终输出应该是这样的:

(title-a , word-a-1 , count-a-1 , word-a-2 , count-a-2 ....)

(title-b , word-b-1, count-b-1 , word-b-2 , count-b-2 ....)
.
.
.
.
(title-x , word-x-1, count-x-1 , word-x-2 , count-x-2 ....)

总而言之,我想在第一个mapreduce的输出记录上分别实现wordcount。有人可以建议我这样做的好方法,或者我如何链接第二个地图减少工作以创建上述输出或更好地格式化它?

以下是代码,从github借用它并进行了一些更改

package com.org;
import javax.xml.stream.XMLStreamConstants;//XMLInputFactory;
import java.io.*;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.io.DataOutputBuffer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.TaskAttemptID;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;               
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import javax.xml.stream.*;

public class XmlParser11
{

    public static class XmlInputFormat1 extends TextInputFormat {

    public static final String START_TAG_KEY = "xmlinput.start";
    public static final String END_TAG_KEY = "xmlinput.end";


    public RecordReader<LongWritable, Text> createRecordReader(
            InputSplit split, TaskAttemptContext context) {
        return new XmlRecordReader();
    }

    /**
     * XMLRecordReader class to read through a given xml document to output
     * xml blocks as records as specified by the start tag and end tag
     *
     */
    // @Override
    public static class XmlRecordReader extends
            RecordReader<LongWritable, Text> {
        private byte[] startTag;
        private byte[] endTag;
        private long start;
        private long end;
        private FSDataInputStream fsin;
        private DataOutputBuffer buffer = new DataOutputBuffer();

        private LongWritable key = new LongWritable();
        private Text value = new Text();
        @Override
        public void initialize(InputSplit split, TaskAttemptContext context)
                throws IOException, InterruptedException {
            Configuration conf = context.getConfiguration();
            startTag = conf.get(START_TAG_KEY).getBytes("utf-8");
            endTag = conf.get(END_TAG_KEY).getBytes("utf-8");
            FileSplit fileSplit = (FileSplit) split;

            // open the file and seek to the start of the split
            start = fileSplit.getStart();
            end = start + fileSplit.getLength();
            Path file = fileSplit.getPath();
            FileSystem fs = file.getFileSystem(conf);
            fsin = fs.open(fileSplit.getPath());
            fsin.seek(start);

        }
    @Override
    public boolean nextKeyValue() throws IOException,
                InterruptedException {
            if (fsin.getPos() < end) {
                if (readUntilMatch(startTag, false)) {
                    try {
                        buffer.write(startTag);
                        if (readUntilMatch(endTag, true)) {
                            key.set(fsin.getPos());
                            value.set(buffer.getData(), 0,
                                    buffer.getLength());
                            return true;
                        }
                    } finally {
                        buffer.reset();
                    }
                }
            }
            return false;
        }
    @Override
    public LongWritable getCurrentKey() throws IOException,
                InterruptedException {
            return key;
        }

    @Override
    public Text getCurrentValue() throws IOException,
                InterruptedException {
            return value;
        }
    @Override
    public void close() throws IOException {
            fsin.close();
        }
    @Override
        public float getProgress() throws IOException {
            return (fsin.getPos() - start) / (float) (end - start);
        }

        private boolean readUntilMatch(byte[] match, boolean withinBlock)
                throws IOException {
            int i = 0;
            while (true) {
                int b = fsin.read();
                // end of file:
                if (b == -1)
                    return false;
                // save to buffer:
                if (withinBlock)
                    buffer.write(b);
                // check if we're matching:
        if (b == match[i]) {
                    i++;
                    if (i >= match.length)
                        return true;
                } else
                    i = 0;
                // see if we've passed the stop point:
                if (!withinBlock && i == 0 && fsin.getPos() >= end)
                    return false;
            }
        }
    }
}


    public static class Map extends Mapper<LongWritable, Text,Text, Text> {
  @Override
  protected void map(LongWritable key, Text value,
                 Mapper.Context context)
  throws
  IOException, InterruptedException {
    String document = value.toString();
    System.out.println("'" + document + "'");
        try {
      XMLStreamReader reader =                      XMLInputFactory.newInstance().createXMLStreamReader(new     
           ByteArrayInputStream(document.getBytes()));
  String propertyName = "";
  String propertyValue = "";
  String currentElement = "";
  while (reader.hasNext()) {
    int code = reader.next();
    switch (code) {
      case XMLStreamConstants.START_ELEMENT: //START_ELEMENT:
        currentElement = reader.getLocalName();
        break;
      case XMLStreamConstants.CHARACTERS:  //CHARACTERS:
        if (currentElement.equalsIgnoreCase("title")) {
          propertyName += reader.getText();
          //System.out.println(propertyName);
        } else if (currentElement.equalsIgnoreCase("text")) {
     propertyValue += reader.getText();
          //System.out.println(propertyValue);
        }
        break;
    }
  }
  reader.close();
  context.write(new Text(propertyName.trim()), new Text(propertyValue.trim()));

}
    catch(Exception e){
            throw new IOException(e);

            }

  }
}
public static class Reduce
extends Reducer<Text, Text, Text, Text> {

  @Override
  protected void setup(
  Context context)
  throws IOException, InterruptedException {
    context.write(new Text("<Start>"), null);
  }

  @Override
  protected void cleanup(
  Context context)
  throws IOException, InterruptedException {
    context.write(new Text("</Start>"), null);
  }

  private Text outputKey = new Text();
  public void reduce(Text key, Iterable<Text> values,
                 Context context)
  throws IOException, InterruptedException {
for (Text value : values) {
      outputKey.set(constructPropertyXml(key, value));
      context.write(outputKey, null);
    }
 }

  public static String constructPropertyXml(Text name, Text value) {
    StringBuilder sb = new StringBuilder();
    sb.append("<property><name>").append(name)
    .append("</name><value>").append(value)
    .append("</value></property>");
    return sb.toString();
  }
}



    public static void main(String[] args) throws Exception
    {
            Configuration conf = new Configuration();

            conf.set("xmlinput.start", "<page>");
            conf.set("xmlinput.end", "</page>");
            Job job = new Job(conf);
            job.setJarByClass(XmlParser11.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(Text.class);

            job.setMapperClass(XmlParser11.Map.class);
            job.setReducerClass(XmlParser11.Reduce.class);

            job.setInputFormatClass(XmlInputFormat1.class);
            job.setOutputFormatClass(TextOutputFormat.class);

            FileInputFormat.addInputPath(job, new Path(args[0]));
            FileOutputFormat.setOutputPath(job, new Path(args[1]));

            job.waitForCompletion(true);
    }
}

我们在网上找到的wordcount代码执行所有文件的单词计数并给出输出。我想分别为每个文本字段执行wordcount。上面的映射器用于从XML文档中提取标题和文本。有没有办法可以在同一个mapper中执行wordcount。如果我这样做,我的下一个疑问是如何将它与已存在的键值对(标题,文本)一起传递给reducer。对不起,我无法正确地表达我的问题,但我想读者必须有一些想法

2 个答案:

答案 0 :(得分:0)

我不确定我是否理解得当。所以我的答案也有很多问题。

首先,编写此代码的人可能会尝试演示如何使用MR编写自定义InputFormat 来处理xml数据。我不知道它与你的问题有什么关系。

总结一下,我想在第一个mapreduce的输出记录上分别实现wordcount。有人可以建议我这样做的好方法

读取第一个MR生成的输出文件并执行此操作。

或如何链接第二张地图缩小作业以创建上述输出或更好地格式化?

通过编写多个驱动程序方法,您可以以这种方式将作业链接在一起,每个作业一个。有关详细信息,请参阅this;有关示例,请参阅this

我想分别为每个文本字段执行wordcount。

单独是什么意思?在传统的wordcount程序中,每个单词的计数是独立计算的。

我有什么方法可以在同一个映射器中执行wordcount。

我希望你能正确理解wordcount程序。在传统的wordcount程序中,您读取输入文件,一次一行,将行切成单词,然后以 1作为值发出每个单词作为键。所有这些都发生在Mapper中,它基本上是相同的Mapper。然后,每个单词的总计数将在您工作的Reducer部分中确定。如果您希望从映射器本身发出带有总计数的单词,则必须在Mapper中读取整个文件并进行计数。为此,您需要在输入格式中将 isSplittable 设置为 false ,以便您的输入文件作为整体读取并转到一个Mapper。

当您从Mapper中发出内容时,如果不是仅限地图作业,则Mapper的输出会自动转到Reducer。你需要别的东西吗?

答案 1 :(得分:0)

我建议你可以使用正则表达式

并执行映射和分组。 在hadoop示例中,jar文件使用此方法提供Grep类,您可以使用正则表达式执行hdfs数据的映射。并对您的maped数据进行分组。