我有一个简单的旧文本文件"行以新行字符结尾。出于任意原因,我需要一次读取和解析此文本文件4(X为通用)行。
我想将Java流用于此任务,我知道我可以将文件转换为如下所示的流:
try (Stream<String> stream = Files.lines(Paths.get("file.txt""))) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
但是我如何使用Java的Stream API来实现&#34;束缚&#34;该文件分为4组连续组?
答案 0 :(得分:4)
这是java.util.Scanner
的工作。在Java 9中,您只需使用
try(Scanner s = new Scanner(PATH)) {
s.findAll("(.*\\R){1,4}")
.map(mr -> Arrays.asList(mr.group().split("\\R")))
.forEach(System.out::println);
}
对于Java 8,您可以使用this answer的findAll
的后端口。为该方法添加import static
后,您可以像
try(Scanner s = new Scanner(PATH)) {
findAll(s, Pattern.compile("(.*\\R){1,4}"))
.map(mr -> Arrays.asList(mr.group().split("\\R")))
.forEach(System.out::println);
}
请注意,匹配操作的结果是一个包含最多四行的字符串(最后一行较少)。如果这适合您的后续操作,您可以跳过将该字符串拆分为单独的行。
您甚至可以使用MatchResult
的属性来更复杂地处理块,例如
try(Scanner s = new Scanner(PATH)) {
findAll(s, Pattern.compile("(.*)\\R(?:(.*)\\R)?(?:(.*)\\R)?(?:(.*)\\R)?"))
.flatMap(mr -> IntStream.rangeClosed(1, 4)
.mapToObj(ix -> mr.group(ix)==null? null: ix+": "+mr.group(ix)))
.filter(Objects::nonNull)
.forEach(System.out::println);
}
答案 1 :(得分:3)
这是使用Guava的Iterators.partition
方法的简单方法:
try (Stream<String> stream = Files.lines(Paths.get("file.txt""))) {
Iterator<List<String>> iterator = Iterators.partition(stream.iterator(), 4);
// iterator.next() returns each chunk as a List<String>
} catch (IOException e) {
// handle exception properly
}
这仅适用于顺序处理,但如果您从磁盘读取文件,我很难想象并行处理会带来什么好处......
编辑:如果你想,而不是使用迭代器,你可以将它再次转换为流:
Stream<List<String>> targetStream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED),
false);
答案 2 :(得分:2)
如果你想坚持使用流,我看到的唯一解决方案是编写自己的自定义收集器。它并非用于此目的,但您可以使用它。
private static final class CustomCollector {
private List<String> list = new ArrayList<>();
private List<String> acumulateList = new ArrayList<>();
public void accept(String str) {
acumulateList.add(str);
if (acumulateList.size() == 4) { // acumulate 4 strings
String collect = String.join("", acumulateList);
// I just joined them in on string, you can do whatever you want
list.add(collect);
acumulateList = new ArrayList<>();
}
}
public CustomCollector combine(CustomCollector other) {
throw new UnsupportedOperationException("Parallel Stream not supported");
}
public List<String> finish() {
if(!acumulateList.isEmpty()) {
list.add(String.join("", acumulateList));
}
return list;
}
public static Collector<String, ?, List<String>> collector() {
return Collector.of(CustomCollector::new, CustomCollector::accept, CustomCollector::combine, CustomCollector::finish);
}
}
并像这样使用它:
stream.collect(CustomCollector.collector());
答案 3 :(得分:2)
如果您已开放使用RxJava,则可以使用其buffer
功能:
Stream<String> stream = Files.lines(Paths.get("file.txt"))
Observable.fromIterable(stream::iterator)
.buffer(4) // Observable<List<String>>
.map(x -> String.join(", ", x)) // Observable<String>
.forEach(System.out::println);
buffer
创建一个Observable
,用于收集特定大小的列表中的元素。在上面的示例中,我通过map
添加了另一个转换,以使列表更适合打印,但您可以根据需要转换Observable
。例如,如果您有一个方法processChunk
以List<String>
作为参数并返回String
,则可以执行以下操作:
Observable<String> fileObs =
Observable.fromIterable(stream::iterator)
.buffer(4)
.map(x -> processChunk(x));
答案 4 :(得分:2)
有一种方法可以使用标准Java 8 Stream API将文件内容分区并处理为import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class ReadFileWithStream {
public static void main(String[] args) throws IOException {
// Path to a file to read
final Path path = Paths.get(ReadFileWithStream.class.getResource("/input.txt").toURI());
final AtomicInteger counter = new AtomicInteger(0);
// Size of a chunk
final int size = 4;
final Collection<List<String>> partitioned = Files.lines(path)
.collect(Collectors.groupingBy(it -> counter.getAndIncrement() / size))
.values();
partitioned.forEach(System.out::println);
}
}
- 大小的块。您可以使用Collectors.groupingBy()
将文件内容分区为块 - 您可以将它们收集为[0, 0, 0, 2]
[0, -3, 2, 0]
[1, -3, -8, 0]
[2, -12, -11, -11]
[-8, -1, -8, 0]
[2, -1, 2, -1]
... and so on
,也可以在收集所有行时应用一些处理(例如,您可以将它们连接到单个字符串)。
看一下下面的例子:
List<String>
我的输入文件contains some numbers (one number at a line),当我运行以下代码时,我会得到类似的内容:
Collection<List<String>>
Collectors.groupingBy()
也允许我使用不同的下游收集器。默认情况下,Collectors.toList()
正在使用,因此我的结果会累积到Collection<Integer>
中,我得到final Collection<Integer> partitioned = Files.lines(path)
.collect(Collectors.groupingBy(it -> counter.getAndIncrement() / size, Collectors.summingInt(Integer::valueOf)))
.values();
作为最终结果。
假设我想要读取4个大小的块,我想将所有数字加在一个块中。在这种情况下,我将使用Collectors.summingInt()
作为我的下游函数,返回的结果为2
-1
-10
-32
-17
2
-11
-49
... and so on
:
Collectors.groupingBy()
输出:
width = 80
height = 80
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=( width, height, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten()) # this converts our 3D feature maps to 1D feature vectors
model.add(Dense(64))
model.add(Activation('relu'))
#model.add(Dropout(0.5))
model.add(Dense(2))
model.add(Activation('softmax'))
model.compile(loss='sparse_categorical_crossentropy',
optimizer='rmsprop',
metrics=['accuracy'])
最后但并非最不重要。 img = image.load_img('Test2.jpg', target_size=(80, 80))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
images = np.vstack([x])
classes = model.predict_proba(images, batch_size=1)
print(classes)
[[ 0. 1.]]
返回一个地图,其中值按特定键分组。这就是为什么最后我们调用Map.values()
来获取此地图中包含的值的集合。
希望它有所帮助。