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.

35
demandé sur H.Z. 2013-09-25 22:28:13

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();
41
répondu Amar 2013-09-25 18:41:39

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");
}
11
répondu Tariq 2013-09-25 20:01:30

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
10
répondu YaOzI 2014-06-22 17:07:36

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();
    ....
}
3
répondu Karl Moad 2015-12-27 09:12:31

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é :-)

2
répondu Nir Hedvat 2016-07-20 13:32:52

Ce qui m'a aidé:

String fileName = ((org.apache.hadoop.mapreduce.lib.input.FileSplit) context.getInputSplit()).getPath().getName();
1
répondu Max Gabderakhmanov 2017-04-14 11:44:49

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()
1
répondu Sainagaraju Vaduka 2017-08-28 10:54:10

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() { }

}
1
répondu Hans Brende 2018-03-27 01:27:25

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();
0
répondu Tulio Braga 2015-11-25 05:39:52
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.

0
répondu nimbus_debug 2018-08-01 09:28:06