Comment obtenir le nom du fichier d'entrée dans le mapper dans un programme Hadoop?
Comment je peux obtenir le nom du fichier d'entrée dans un mappeur? J'ai plusieurs fichiers d'entrée, stocké dans le répertoire d'entrée, chaque mappeur peut lire un fichier différent, et j'ai besoin de savoir quel fichier le mappeur a lire.
10 réponses
tout d'abord, vous devez obtenir la division d'entrée, en utilisant la nouvelle API mapreduce, il serait fait comme suit:
context.getInputSplit();
mais pour obtenir le chemin du fichier et le nom du fichier, vous devez d'abord taper le résultat dans FileSplit.
Donc, pour obtenir l'entrée chemin du fichier que vous pouvez faire ce qui suit:
Path filePath = ((FileSplit) context.getInputSplit()).getPath();
String filePathString = ((FileSplit) context.getInputSplit()).getPath().toString();
de même, pour obtenir le nom du fichier, vous pouvez simplement faire appel à getName (), comme ceci:
String fileName = ((FileSplit) context.getInputSplit()).getPath().getName();
utilisez ceci à l'intérieur de votre mapper:
FileSplit fileSplit = (FileSplit)context.getInputSplit();
String filename = fileSplit.getPath().getName();
Edit :
Essayez ceci si vous voulez le faire à l'intérieur de configure () dans ancienne API:
String fileName = new String();
public void configure(JobConf job)
{
filename = job.get("map.input.file");
}
Si vous utilisez Hadoop Streaming, vous pouvez utiliser le variables JobConf d'un travail de streaming mapper/reducer.
en ce qui concerne le nom du fichier d'entrée de mapper, voir le Paramètres Configurés, le map.input.file
variable (le nom du fichier que la carte lit de) est celui qui peut faire le travail. Mais notez que:
Note: lors de l'exécution d'une tâche de streaming, les noms du " mapred" les paramètres sont transformés. Le nombre de points ( . ) deviennent des traits de soulignement ( _ ). Par exemple, mapred.job.id devient mapred_job_id et mapred.jar devient mapred_jar. Pour obtenir les valeurs dans le mapper/reducer d'une tâche de streaming, utilisez les noms de paramètres avec les underscores.
Par exemple, si vous utilisez Python, alors vous pouvez mettre cette ligne dans votre fichier mappeur:
import os
file_name = os.getenv('map_input_file')
print file_name
Remarqué sur Hadoop 2.4 et une plus grande utilisation de l' vieux api cette méthode produit une valeur nulle
String fileName = new String();
public void configure(JobConf job)
{
fileName = job.get("map.input.file");
}
vous pouvez aussi utiliser L'objet Reporter passé à votre fonction map pour obtenir le InputSplit et le cast à un FileSplit pour récupérer le nom de fichier
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();
....
}
si vous utilisez L'InputFormat régulier, utilisez ceci dans votre 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()
si vous utilisez CombineFileInputFormat, c'est une approche différente car elle combine plusieurs petits fichiers en un seul fichier relativement gros (dépend de votre configuration). Le Mapper et le RecordReader s'exécutent tous les deux sur le même JVM pour que vous puissiez passer des données entre eux lors de l'exécution. Vous devez implémenter votre propre CombineFileRecordReaderWrapper et faire comme suit:
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;
}
...
Puis, dans votre Mappeur, utilisez ceci:
String currentFileName = MyCombineFileRecordReaderWrapper.getCurrentFilePath()
j'Espère que j'ai aidé :-)
Ce qui m'a aidé:
String fileName = ((org.apache.hadoop.mapreduce.lib.input.FileSplit) context.getInputSplit()).getPath().getName();
vous devez d'abord convertir en InputSplit par typographie et ensuite vous devez taper cast to FileSplit.
Exemple:
InputSplit inputSplit= (InputSplit)context.getInputSplit();
Path filePath = ((FileSplit) inputSplit).getPath();
String filePathString = ((FileSplit) context.getInputSplit()).getPath().toString()
Les réponses qui prônent la coulée FileSplit
ne fonctionne plus, comme FileSplit
les instances ne sont plus retournées pour des entrées multiples (vous obtiendrez donc un ClassCastException
). Au lieu de cela, org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit
les instances sont retournées. Malheureusement, le TaggedInputSplit
la classe n'est pas accessible sans réflexion. Voici un cours sur les utilitaires que j'ai écrit pour ça. Juste à faire:
Path path = MapperUtils.getPath(context.getInputSplit());
dans votre Mapper.setup(Context context)
méthode.
Voici le code source de mon MapperUtils
catégorie:
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() { }
}
org.apache.hadood.mapred
paquet la signature de la fonction map doit être:
map(Object, Object, OutputCollector, Reporter)
ainsi, pour obtenir le nom du fichier à l'intérieur de la fonction map, vous pouvez utiliser L'objet Reporter comme ceci:
String fileName = ((FileSplit) reporter.getInputSplit()).getPath().getName();
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() { }
}
j'ai réécrit le code fourni par hans-brende dans Java 7, ça a marché. Mais il y a un problème
Compteurs De Format D'Entrée De Fichier Octets Lus=0 Les octets lus sont nuls si on utilise des entrées multiples.