it-swarm.com.de

Wie bekomme ich den Namen der Eingabedatei in einem Hadoop-Programm im Mapper?

Wie kann ich den Namen der Eingabedatei in einem Mapper erhalten? Ich habe mehrere Eingabedateien im Eingabeverzeichnis gespeichert, jeder Mapper kann eine andere Datei lesen, und ich muss wissen, welche Datei der Mapper gelesen hat.

38
H.Z.

Zuerst müssen Sie die Eingabeaufteilung abrufen, indem Sie die neuere Mapreduce-API verwenden. Gehen Sie dazu wie folgt vor:

context.getInputSplit();

Um jedoch den Dateipfad und den Dateinamen zu erhalten, müssen Sie das Ergebnis zuerst in FileSplit eingeben.

Um den Pfad der Eingabedatei zu erhalten, können Sie Folgendes tun:

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

Um den Dateinamen abzurufen, rufen Sie getName () wie folgt auf:

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

Verwenden Sie dies in Ihrem Mapper:

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

Bearbeiten:

Versuchen Sie dies, wenn Sie es in configure () über die alte API tun wollen:

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

Wenn Sie Hadoop Streaming verwenden, können Sie die JobConf-Variablen im Mapper/Reducer eines Streaming-Jobs verwenden.

Den Namen der Eingabedatei von Mapper finden Sie im Abschnitt Configured Parameters . Die Variable map.input.file (der Dateiname, den die Map aus liest) kann die Jobs erledigen. Beachten Sie jedoch Folgendes:

Hinweis: Während der Ausführung eines Streaming-Jobs werden die Namen der Parameter "mapred" transformiert. Die Punkte (.) Werden zu Unterstrichen (_). Beispielsweise wird mapred.job.id zu mapred_job_id und mapred.jar zu mapred_jar. Um die Werte im Mapper/Reducer eines Streaming-Jobs abzurufen, verwenden Sie die Parameternamen mit den Unterstrichen.


Wenn Sie beispielsweise Python verwenden, können Sie diese Zeile in Ihre Mapper-Datei einfügen:

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

Wenn Sie das reguläre InputFormat verwenden, verwenden Sie dies in Ihrem 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()

Wenn Sie CombineFileInputFormat verwenden, handelt es sich um einen anderen Ansatz, da mehrere kleine Dateien in einer relativ großen Datei zusammengefasst werden (abhängig von Ihrer Konfiguration). Sowohl Mapper als auch RecordReader werden in derselben JVM ausgeführt, sodass Sie bei der Ausführung Daten zwischen ihnen übergeben können. Sie müssen einen eigenen CombineFileRecordReaderWrapper implementieren und wie folgt vorgehen:

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;
    }
...

Dann verwenden Sie in Ihrem Mapper folgendes:

String currentFileName = MyCombineFileRecordReaderWrapper.getCurrentFilePath()

Hoffe ich habe geholfen :-)

4
Nir Hedvat

Bei Hadoop 2.4 und höher mit der API old wird diese Methode als Nullwert bezeichnet

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

Alternativ können Sie das an Ihre Map-Funktion übergebene Reporter-Objekt verwenden, um InputSplit abzurufen und in FileSplit zu konvertieren, um den Dateinamen abzurufen

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

Sie müssen zuerst durch Typumwandlung in InputSplit konvertieren, und dann müssen Sie die Umwandlung in FileSplit eingeben.

Beispiel:

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

Das hat mir geholfen:

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

Die Antworten, die die Umwandlung in FileSplit befürworten, funktionieren nicht mehr, da FileSplit-Instanzen nicht mehr für mehrere Eingaben zurückgegeben werden (Sie erhalten also eine ClassCastException). Stattdessen werden org.Apache.hadoop.mapreduce.lib.input.TaggedInputSplit-Instanzen zurückgegeben. Leider ist die TaggedInputSplit-Klasse nicht ohne Reflektion zugänglich. Also hier ist eine Utility-Klasse, die ich dafür geschrieben habe. Mach einfach:

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

in Ihrer Mapper.setup(Context context)-Methode.

Hier ist der Quellcode für meine MapperUtils-Klasse:

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

Für org.Apache.hadood.mapred package sollte die Map-Funktionssignatur lauten:

map(Object, Object, OutputCollector, Reporter) 

Um den Dateinamen in die Kartenfunktion zu bekommen, können Sie das Reporter-Objekt folgendermaßen verwenden:

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

}

Ich schreibe den Code, den hans-brende in Java 7 bereitstellt, neu, es hat funktioniert 

Dateieingangsformatzähler Bytes Read = 0 Bytes Read ist null, wenn MultipleInputs verwendet werden.

0
nimbus_debug