web-dev-qa-db-fra.com

Comment obtenir le nom du fichier d'entrée dans le mappeur dans un programme Hadoop?

Comment puis-je obtenir le nom du fichier d'entrée dans un mappeur? J'ai plusieurs fichiers d'entrée stockés 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 lu.

38
H.Z.

Vous devez d’abord obtenir le fractionnement en entrée. En utilisant la nouvelle API mapreduce, vous procéderez comme suit:

context.getInputSplit();

Mais pour obtenir le chemin du fichier et le nom du fichier, vous devrez d'abord convertir le résultat dans FileSplit.

Donc, afin d’obtenir le chemin du fichier d’entrée, 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 appeler getName (), comme ceci:

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

Utilisez ceci dans votre mappeur:

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

Modifier :

Essayez ceci si vous voulez le faire dans configure () par le biais de old API :

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

Si vous utilisez Hadoop Streaming , vous pouvez utiliser les variables JobConf dans le mappeur/réducteur d'un travail en streaming.

En ce qui concerne le nom de fichier d'entrée du mappeur, voir la section Paramètres configurés , la variable map.input.file (le nom du fichier que la carte lit de) est celui qui permet d'effectuer les tâches. Mais notez que:

Remarque: lors de l'exécution d'un travail de diffusion en continu, les noms des paramètres "mapred" sont transformés. Les 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 mappeur/réducteur d'un travail de diffusion, utilisez les noms de paramètre avec les traits de soulignement.


Par exemple, si vous utilisez Python, vous pouvez insérer cette ligne dans votre fichier de mappeur:

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

Si vous utilisez le format standard InputFormat, utilisez-le dans votre mappeur:

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, l'approche est différente car elle combine plusieurs petits fichiers dans un fichier relativement volumineux (cela dépend de votre configuration). Le mappeur et RecordReader s'exécutent tous deux sur la même machine virtuelle afin de pouvoir transmettre des données entre eux lors de l'exécution de . Vous devez implémenter votre propre CombineFileRecordReaderWrapper et procéder 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;
    }
...

Ensuite, dans votre mappeur, utilisez ceci:

String currentFileName = MyCombineFileRecordReaderWrapper.getCurrentFilePath()

J'espère que j'ai aidé :-)

4
Nir Hedvat

Remarqué sur Hadoop 2.4 et versions supérieures en utilisant old 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 également utiliser l'objet Reporter transmis à votre fonction de carte pour obtenir InputSplit et le transtyper en 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();
    ....
}
3
Karl Moad

Vous devez d'abord convertir en InputSplit par conversion de type, puis taper cast en FileSplit.

Exemple:

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

Cela m'a aidé:

String fileName = ((org.Apache.hadoop.mapreduce.lib.input.FileSplit) context.getInputSplit()).getPath().getName();
1
Max Gabderakhmanov

Les réponses qui préconisent de transtyper FileSplit ne fonctionneront plus car les instances FileSplit ne sont plus renvoyées pour plusieurs entrées (vous obtiendrez donc une ClassCastException). À la place, les instances org.Apache.hadoop.mapreduce.lib.input.TaggedInputSplit sont renvoyées. Malheureusement, la classe TaggedInputSplit n'est pas accessible sans utiliser la réflexion. Donc, voici une classe utilitaire que j'ai écrite pour cela. Il suffit de faire:

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

dans votre méthode Mapper.setup(Context context).

Voici le code source de ma classe 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() { }

}
1
Hans Brende

Pour org.Apache.hadood.mapred package, la signature de la fonction map doit être:

map(Object, Object, OutputCollector, Reporter) 

Donc, pour obtenir le nom du fichier dans la fonction map, vous pouvez utiliser l’objet Reporter comme ceci:

String fileName = ((FileSplit) reporter.getInputSplit()).getPath().getName();
0
Tulio Braga
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() { }

}

Je réécris le code que hans-brende fournit en Java 7, cela a fonctionné . 

Compteurs de format d'entrée de fichier Octets lus = 0 Octets lus est zéro si vous utilisez MultipleInputs.

0
nimbus_debug