복붙노트

[HADOOP] Hadoop 프로그램의 매퍼에서 입력 파일 이름을 가져 오는 방법은 무엇입니까?

HADOOP

Hadoop 프로그램의 매퍼에서 입력 파일 이름을 가져 오는 방법은 무엇입니까?

mapper 내에서 입력 파일의 이름을 어떻게 얻을 수 있습니까? 입력 된 디렉토리에 여러 개의 입력 파일이 저장되어 있으며 각 매퍼는 다른 파일을 읽을 수 있으며 매퍼가 읽은 파일을 알아야합니다.

해결법

  1. ==============================

    1.먼저 다음과 같이 수행 될 새로운 mapreduce API를 사용하여 입력 분할을 가져와야합니다.

    먼저 다음과 같이 수행 될 새로운 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();
    
  2. ==============================

    2.당신의 매퍼 안에서 이것을 사용하십시오 :

    당신의 매퍼 안에서 이것을 사용하십시오 :

    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");
    }
    
  3. ==============================

    3.Hadoop Streaming을 사용하는 경우 스트리밍 작업의 매퍼 / 감속기에서 JobConf 변수를 사용할 수 있습니다.

    Hadoop Streaming을 사용하는 경우 스트리밍 작업의 매퍼 / 감속기에서 JobConf 변수를 사용할 수 있습니다.

    맵퍼의 입력 파일 이름은 Configured Parameters 섹션을 참조하십시오. map.input.file 변수 (맵을 읽는 파일 이름)는 작업을 완료 할 수있는 변수입니다. 그러나 유의 사항 :

    예를 들어, 파이썬을 사용하고 있다면 다음 행을 매퍼 파일에 넣을 수 있습니다 :

    import os
    file_name = os.getenv('map_input_file')
    print file_name
    
  4. ==============================

    4.일반 InputFormat을 사용하는 경우 Mapper에서 다음을 사용하십시오.

    일반 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()
    

    희망은 내가 도왔 :-)

  5. ==============================

    5.Hadoop 2.4 이상에서는 이전 API를 사용하여이 메소드가 null 값을 생성합니다.

    Hadoop 2.4 이상에서는 이전 API를 사용하여이 메소드가 null 값을 생성합니다.

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

    또는지도 함수에 전달 된 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();
        ....
    }
    
  6. ==============================

    6.이것은 나를 도왔다.

    이것은 나를 도왔다.

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

    7.먼저 타입 캐스팅을 통해 InputSplit으로 변환해야합니다. 그런 다음 FileSplit에 cast를 입력해야합니다.

    먼저 타입 캐스팅을 통해 InputSplit으로 변환해야합니다. 그런 다음 FileSplit에 cast를 입력해야합니다.

    예:

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

    8.FileSplit 인스턴스가 더 이상 여러 입력에 대해 반환되지 않으므로 FileSplit에 대한 주조를 지원하는 답변이 더 이상 작동하지 않습니다 (따라서 ClassCastException이 발생합니다). 대신 org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit 인스턴스가 리턴됩니다. 불행히도 Reflection을 사용하지 않으면 TaggedInputSplit 클래스에 액세스 할 수 없습니다. 여기에 제가 작성한 유틸리티 클래스가 있습니다. 그냥 해:

    FileSplit 인스턴스가 더 이상 여러 입력에 대해 반환되지 않으므로 FileSplit에 대한 주조를 지원하는 답변이 더 이상 작동하지 않습니다 (따라서 ClassCastException이 발생합니다). 대신 org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit 인스턴스가 리턴됩니다. 불행히도 Reflection을 사용하지 않으면 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() { }
    
    }
    
  9. ==============================

    9.org.apache.hadood.mapred 패키지의 경우 맵 함수 서명은 다음과 같아야합니다.

    org.apache.hadood.mapred 패키지의 경우 맵 함수 서명은 다음과 같아야합니다.

    map(Object, Object, OutputCollector, Reporter) 
    

    따라서 맵 함수 내에서 파일 이름을 얻으려면 다음과 같이 Reporter 객체를 사용할 수 있습니다.

    String fileName = ((FileSplit) reporter.getInputSplit()).getPath().getName();
    
  10. ==============================

    10.

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

    나는 Java 7에서 제공하는 코드 인 hans-brende를 다시 작성했다. 그러나 문제는

    파일 입력 형식 카운터         바이트 읽기 = 0 MultipleInputs를 사용하면 Bytes Read가 0입니다.

  11. from https://stackoverflow.com/questions/19012482/how-to-get-the-input-file-name-in-the-mapper-in-a-hadoop-program by cc-by-sa and MIT license