我有以下要求(使用大文本文件): 给定 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);
}
}
}
提前致谢
谢谢@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);
}
}