如何在映射器中获取输入文件的名称?我有多个输入文件存储在输入目录中,每个映射器可能会读取不同的文件,我需要知道映射器已读取的文件。
答案 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 中编写了一个过程