如何在Hadoop程序中的映射器中获取输入文件名?

时间:2013-09-25 18:28:14

标签: hadoop mapreduce

如何在映射器中获取输入文件的名称?我有多个输入文件存储在输入目录中,每个映射器可能会读取不同的文件,我需要知道映射器已读取的文件。

11 个答案:

答案 0 :(得分:42)

首先,您需要使用较新的mapreduce API获取输入拆分,如下所示:

context.getInputSplit();

但是为了获取文件路径和文件名,您需要先将结果强制转换为FileSplit。

因此,为了获得输入文件路径,您可以执行以下操作:

Path filePath = ((FileSplit) context.getInputSplit()).getPath();
String filePathString = ((FileSplit) context.getInputSplit()).getPath().toString();

同样,要获取文件名,您可以调用getName(),如下所示:

String fileName = ((FileSplit) context.getInputSplit()).getPath().getName();

答案 1 :(得分:13)

在mapper中使用:

FileSplit fileSplit = (FileSplit)context.getInputSplit();
String filename = fileSplit.getPath().getName();

修改:

如果您想通过旧API configure()中执行此操作:

String fileName = new String();
public void configure(JobConf job)
{
   filename = job.get("map.input.file");
}

答案 2 :(得分:10)

如果您使用的是Hadoop Streaming,则可以使用JobConf variables in a streaming job's mapper/reducer.

对于mapper的输入文件名,请参阅Configured Parameters部分,map.input.file变量(地图从读取的文件名)是可以得到的完成的工作。但请注意:

  

注意:在执行流媒体作业期间," mapred"参数被转换。点(。)变为下划线(_)。例如,mapred.job.id变为mapred_job_id,mapred.jar变为mapred_jar。要获取流作业的映射器/缩减器中的值,请使用带下划线的参数名称。


例如,如果您使用的是Python,则可以将此行放在映射文件中:

import os
file_name = os.getenv('map_input_file')
print file_name

答案 3 :(得分:4)

如果您正在使用常规的InputFormat,请在Mapper中使用它:

InputSplit is = context.getInputSplit();
Method method = is.getClass().getMethod("getInputSplit");
method.setAccessible(true);
FileSplit fileSplit = (FileSplit) method.invoke(is);
String currentFileName = fileSplit.getPath().getName()

如果您正在使用CombineFileInputFormat,它是一种不同的方法,因为它将几个小文件合并为一个相对较大的文件(取决于您的配置)。 Mapper和RecordReader都在同一个JVM上运行,因此您可以在运行时在它们之间传递数据。 您需要实现自己的CombineFileRecordReaderWrapper,并执行以下操作:

public class MyCombineFileRecordReaderWrapper<K, V> extends RecordReader<K, V>{
...
private static String mCurrentFilePath;
...
public void initialize(InputSplit combineSplit , TaskAttemptContext context) throws IOException, InterruptedException {
        assert this.fileSplitIsValid(context);
        mCurrentFilePath = mFileSplit.getPath().toString();
        this.mDelegate.initialize(this.mFileSplit, context);
    }
...
public static String getCurrentFilePath() {
        return mCurrentFilePath;
    }
...

然后,在Mapper中,使用:

String currentFileName = MyCombineFileRecordReaderWrapper.getCurrentFilePath()

希望我帮助过: - )

答案 4 :(得分:3)

使用 api在Hadoop 2.4及更高版本上注意到此方法产生空值

String fileName = new String();
public void configure(JobConf job)
{
   fileName = job.get("map.input.file");
}

或者,您可以利用传递给map函数的Reporter对象来获取InputSplit并转换为FileSplit以检索文件名

public void map(LongWritable offset, Text record,
        OutputCollector<NullWritable, Text> out, Reporter rptr)
        throws IOException {

    FileSplit fsplit = (FileSplit) rptr.getInputSplit();
    String inputFileName = fsplit.getPath().getName();
    ....
}

答案 5 :(得分:1)

您必须首先通过类型转换转换为InputSplit,然后您需要输入强制转换为FileSplit。

示例:

InputSplit inputSplit= (InputSplit)context.getInputSplit();
Path filePath = ((FileSplit) inputSplit).getPath();
String filePathString = ((FileSplit) context.getInputSplit()).getPath().toString()

答案 6 :(得分:1)

这对我有所帮助:

String fileName = ((org.apache.hadoop.mapreduce.lib.input.FileSplit) context.getInputSplit()).getPath().getName();

答案 7 :(得分:1)

主张投放到FileSplit的答案将不再有效,因为多个输入不再返回FileSplit个实例(因此您将获得ClassCastException)。而是返回org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit个实例。遗憾的是,如果不使用反射,则无法访问TaggedInputSplit类。所以这是我为此写的实用程序类。只是做:

Path path = MapperUtils.getPath(context.getInputSplit());

Mapper.setup(Context context)方法中。

以下是我的MapperUtils课程的源代码:

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Optional;

public class MapperUtils {

    public static Path getPath(InputSplit split) {
        return getFileSplit(split).map(FileSplit::getPath).orElseThrow(() -> 
            new AssertionError("cannot find path from split " + split.getClass()));
    }

    public static Optional<FileSplit> getFileSplit(InputSplit split) {
        if (split instanceof FileSplit) {
            return Optional.of((FileSplit)split);
        } else if (TaggedInputSplit.clazz.isInstance(split)) {
            return getFileSplit(TaggedInputSplit.getInputSplit(split));
        } else {
            return Optional.empty();
        }
    }

    private static final class TaggedInputSplit {
        private static final Class<?> clazz;
        private static final MethodHandle method;

        static {
            try {
                clazz = Class.forName("org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit");
                Method m = clazz.getDeclaredMethod("getInputSplit");
                m.setAccessible(true);
                method = MethodHandles.lookup().unreflect(m).asType(
                    MethodType.methodType(InputSplit.class, InputSplit.class));
            } catch (ReflectiveOperationException e) {
                throw new AssertionError(e);
            }
        }

        static InputSplit getInputSplit(InputSplit o) {
            try {
                return (InputSplit) method.invokeExact(o);
            } catch (Throwable e) {
                throw new AssertionError(e);
            }
        }
    }

    private MapperUtils() { }

}

答案 8 :(得分:0)

对于org.apache.hadood.mapred包,地图功能签名应为:

map(Object, Object, OutputCollector, Reporter) 

因此,要在map函数中获取文件名,可以像这样使用Reporter对象:

String fileName = ((FileSplit) reporter.getInputSplit()).getPath().getName();

答案 9 :(得分:0)

package com.foo.bar;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;

public class MapperUtils {

    public static Path getPath(InputSplit split) {
        FileSplit fileSplit = getFileSplit(split);
        if (fileSplit == null) {
            throw new AssertionError("cannot find path from split " + split.getClass());
        } else {
            return fileSplit.getPath();
        }
    }

    public static FileSplit getFileSplit(InputSplit split) {
        if (split instanceof FileSplit) {
            return (FileSplit)split;
        } else if (TaggedInputSplit.clazz.isInstance(split)) {
            return getFileSplit(TaggedInputSplit.getInputSplit(split));
        } else {
            return null;
        }
    }

    private static final class TaggedInputSplit {
        private static final Class<?> clazz;
        private static final MethodHandle method;

        static {
            try {
                clazz = Class.forName("org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit");
                Method m = clazz.getDeclaredMethod("getInputSplit");
                m.setAccessible(true);
                method = MethodHandles.lookup().unreflect(m).asType(
                    MethodType.methodType(InputSplit.class, InputSplit.class));
            } catch (ReflectiveOperationException e) {
                throw new AssertionError(e);
            }
        }

        static InputSplit getInputSplit(InputSplit o) {
            try {
                return (InputSplit) method.invokeExact(o);
            } catch (Throwable e) {
                throw new AssertionError(e);
            }
        }
    }

    private MapperUtils() { }

}

我重写了hans-brende在Java 7中提供的代码,它起作用了。 但是存在一个问题

文件输入格式计数器         读取字节数= 0 如果使用MultipleInputs,则读取的字节为零。

答案 10 :(得分:0)

像这样的多个输入:

-Dwordcount.case.sensitive=false
hdfs://192.168.178.22:9000/user/hduser/inWiki
hdfs://192.168.178.22:9000/user/hduser/outWiki1
hdfs://192.168.178.22:9000/user/joe/wordcount/dict/dictionary.txt
-skip hdfs://192.168.178.22:9000/user/joe/wordcount/patterns.txt

对于文件 dictionary.txt,我在 Map Code 中编写了一个过程