如何在Hadoop中创建自定义输出格式

时间:2015-04-13 18:30:30

标签: java hadoop output

我正在尝试创建字数hadoop程序的变体,其中它读取目录中的多个文件并输出每个单词的频率。问题是,我希望它输出一个单词后跟文件名来自该文件的频率。例如:

word1
( file1, 10)
( file2, 3)
( file3, 20)

所以对于word1(说“和”这个词)。它找到10次是file1,在file2中找到3次,等等。现在它只输出一个键值对

 StringTokenizer itr = new StringTokenizer(chapter);
  while (itr.hasMoreTokens()) {
    word.set(itr.nextToken());

    context.write(word, one);

我可以通过

获取文件名
String fileName = ((FileSplit) context.getInputSplit()).getPath().getName();

但我不明白如何格式化我想要的方式。我一直在研究OutputCollector,但我不确定如何正确使用它。

编辑:这是我的mapper和recuder

public static class TokenizerMapper
   extends Mapper<Object, Text, Text, Text>{ 

private Text word = new Text();

public void map(Object key, Text value, Context context
                ) throws IOException, InterruptedException {

  //Take out all non letters and make all lowercase
  String chapter = value.toString();
  chapter = chapter.toLowerCase();
  chapter = chapter.replaceAll("[^a-z]"," ");

  //This is the file name
  String fileName = ((FileSplit) context.getInputSplit()).getPath().getName();

  StringTokenizer itr = new StringTokenizer(chapter);
  while (itr.hasMoreTokens()) {
    word.set(itr.nextToken());

   context.write(word, new Text(fileName)); //
  }
}
  }


  public static class IntSumReducer
       extends Reducer<Text,Text,Text,Text> { second


   public void reduce(Text key, Iterable<Text> values, Context context)
         throws IOException, InterruptedException {

  Map<String, Integer> files = new HashMap<String, Integer>();

 for (Text val : values) {
    if (files.containsKey(val.toString())) {
        files.put(val.toString(), files.get(val.toString())+1);
    } else {
        files.put(val.toString(), 1); 
    }
}

String outputString="";

for (String file : files.keySet()) { 
    outputString = outputString + "\n<" + file + ", " + files.get(file) + ">"; //files.get(file)
}

context.write(key, new Text(outputString));
}

  }

这是输出单词“a”,例如:

a   
(
(chap02, 53), 1)
(
(chap18, 50), 1)

我不确定为什么它为每个条目创建一个键值为1的键值。

1 个答案:

答案 0 :(得分:1)

我认为你根本不需要自定义输出格式。只要将文件名传递给reducer,就可以通过修改TextOutputFormat类型操作中使用的String来完成此操作。说明如下。

在mapper中获取文件名,并将其附加到textInputFormat,如下所示

String fileName = ((FileSplit) context.getInputSplit()).getPath().getName();
context.write(key,new Text(fileName));

然后在reducer中执行以下操作:

public void reduce(Text key, Iterable<Text> values, Context context)
        throws IOException, InterruptedException {
    Map<String, Integer> files = new HashMap<String, Integer>();
    for (Text val : values) {
        if (files.containsKey(val.toString())) {
            files.put(val.toString(), files.get(val.toString()) + 1);
        } else {
            files.put(val.toString(), 1);
        }
    }

    String outputString = key.toString();

    for (String file : files.keySet()) {
        outputString += "\n( " + file + ", " + files.get(file) + ")";
    }

    context.write(key, new Text(outputString));
}

此缩减器将"\n"附加到每行的开头,以强制显示格式完全符合您的要求。

这似乎比编写自己的outputformat简单得多。