如何使用 Java Streams 连接两个文本文件中的行

问题描述 投票:0回答:1

我有以下要求(使用文本文件): 给定 sourceFile1 和 sourceFile2,对于两个文件中的每一行,连接值(逐行)并将它们放入 targetFile 中。

例如:拨打

FileMergerImpl.merge(sourceFile1,sourceFile2,targetFile, String::concat);
on

sourceFile1:
Line1
Line2
Line3

sourceFile2:
A
B

应该导致:

targetFile:
Line1A
Line2B
Line3

问题: 仅使用 java 流(特别是使用

java.nio.file.Files()
)可以实现这一点吗?

我想用

flatMap()
Stream.of(Files.lines(sourceFilename1), Files.lines(sourceFilename2))
打电话。挑战在于我不知道如何将每个流中的条目连接到单个流上。

这是我的代码解决方案,不是使用Java Streams:

package com.example.codingTest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.BinaryOperator;

public class FileMergerImpl {

    public void merge(Path sourceFilename1, Path sourceFilename2, Path targetFilename, BinaryOperator<String> lineCombiner) {

        try (BufferedReader file1Reader = Files.newBufferedReader(sourceFilename1);
             BufferedReader file2Reader = Files.newBufferedReader(sourceFilename2);
             BufferedWriter targetFileWriter = Files.newBufferedWriter(targetFilename)) {

            boolean areThereLinesLeftToRead = true;
            while (areThereLinesLeftToRead) {
                String file1Line = file1Reader.readLine();
                String file2Line = file2Reader.readLine();

                if (file1Line != null && file2Line != null) {
                    targetFileWriter.write(lineCombiner.apply(file1Line, file2Line));
                } else if (file1Line != null) {
                    targetFileWriter.write(file1Line);
                } else if (file2Line != null) {
                    targetFileWriter.write(file2Line);
                } else {
                    areThereLinesLeftToRead = false;
                }
                targetFileWriter.newLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

这里是单元测试代码,对于那些愿意快速运行的人:

package com.example.codingTest;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;

import java.nio.file.Path;

import static com.example.codingTest.TestUtil.*;
import static org.junit.jupiter.api.Assertions.assertEquals;


@ExtendWith(MockitoExtension.class)
public class FileMergerTest {

    @Autowired
    @InjectMocks 
    private FileMergerImpl fileMerger;

    /**
     * Given File1 content:
     * Line1
     * Line2
     * Line3
     * and file2 content:
     * A
     * B
     * Expect content to be
     * Line1A
     * Line2B
     * Line3
     */
    @Test
    public void givenTwoFilesAndThatFirstFileIsLongerThanSecondOneThenMergeContentIntoTarget(){
        Path sourceFile1= createPopulatedFile1(true);
        Path sourceFile2= createPopulatedFile2(false);
        Path targetFile = createEmptyFile();

        fileMerger.merge(sourceFile1,sourceFile2,targetFile, String::concat);

        assertEquals("Line1A", getContentFrom(targetFile).get(0));
        assertEquals("Line2B", getContentFrom(targetFile).get(1));
        assertEquals("Line3", getContentFrom(targetFile).get(2));

        deleteTemporaryFile(sourceFile1);
        deleteTemporaryFile(sourceFile2);
        deleteTemporaryFile(targetFile);
    }
}

package com.example.codingTest;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class TestUtil {

    public static Path createPopulatedFile1(boolean isAddExtraLine)  {
        Path file1;
        BufferedWriter writer = null;
        try{
            file1 = Files.createTempFile(Path.of("./src/test/resources"),"tempfile_1", ".tmp");
           writer = Files.newBufferedWriter(file1);
                writer.write("Line1");
                writer.newLine();
                writer.write("Line2");
                writer.newLine();
                if(isAddExtraLine) {
                    writer.write("Line3");
                    writer.newLine();
                }
             writer.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return file1;
    }

    public static Path createPopulatedFile2(boolean isAddExtraLine)  {
        Path file2;
        BufferedWriter writer = null;
        try{
            file2 = Files.createTempFile(Path.of("./src/test/resources"),"tempfile_1", ".tmp");
            writer = Files.newBufferedWriter(file2);
            writer.write("A");
            writer.newLine();
            writer.write("B");
            writer.newLine();
            if(isAddExtraLine) {
                writer.write("C");
                writer.newLine();
            }
            writer.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return file2;
    }

    public static void deleteTemporaryFile(Path path){
        try {
            Files.delete(path);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static Path createEmptyFile(){
        Path tempEmptyFile;
        try{
            tempEmptyFile = Files.createTempFile(Path.of("./src/test/resources"),"tempEmptyFile", ".tmp");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return tempEmptyFile;
    }

    public static List<String> getContentFrom(Path targetFile){
        try {
            return Files.readAllLines(targetFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

提前致谢

java
1个回答
0
投票

谢谢@Turamarth 为我指明了正确的方向。 仅使用 Java util 库,可能的解决方案如下:

package com.example.codingTest;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class FileMergerImpl {

    public void merge(Path sourceFilename1, Path sourceFilename2, Path targetFilename, BinaryOperator<String> lineCombiner) {

        try(Stream<String> file1Content = Files.lines(sourceFilename1);
            Stream<String> file2Content = Files.lines(sourceFilename2);
            BufferedWriter targetFileWriter = Files.newBufferedWriter(targetFilename)){
            Stream<String> concatenatedStream = zip(file1Content, file2Content, lineCombiner);

            concatenatedStream.forEach((lines) ->
            {
                try {
                    targetFileWriter.write(lines);
                    targetFileWriter.newLine();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static Stream<String> zip(Stream<String> a,
                                     Stream<String> b,
                                     BinaryOperator<java.lang.String> lineCombiner) {

        Iterator<String> aIterator = a.iterator();
        Iterator<String> bIterator = b.iterator();

        Iterator<String> cIterator = new Iterator<>() {
            @Override
            public boolean hasNext() {
                return aIterator.hasNext() || bIterator.hasNext();
            }

            @Override
            public String next() {
                String source1 = "";
                String source2 = "";
                if (aIterator.hasNext()) {
                    source1 = aIterator.next();
                }
                if (bIterator.hasNext()) {
                    source2 = bIterator.next();
                }
                return lineCombiner.apply(source1, source2);
            }
        };
        Spliterator<String> split = Spliterators.spliterator(cIterator, -1, Spliterator.SIZED);
        return StreamSupport.stream(split, false);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.