我必须处理包含预定义记录布局的固定宽度文件,存在多种类型的记录并且记录的第一个字符决定其类型。因为它是固定宽度的,所以不可能总是在一行中放满整个记录类型,所以第二个字符是记录的序号。例如:
0This is the header record------------------------------------
1This is another record always existing out of one lin--------
21This is a record that can be composed out of multiple parts.
22This is the second part of record type 2--------------------
21This is a new record of type 2, first part.-----------------
22This is the second part of record type 2--------------------
23This is the third part of record type 2---------------------
...
使用 Stream API,我想解析这个文件:
Stream<String> lines = Files.lines(Paths.get(args[1]));
lines.map(line -> RecordFactory.createRecord(line)).collect(Collectors.toList());
但是由于这个流是逐行传递的,当它解析记录类型 2 的第一行(记录类型 2 序列 1)时,记录 2 的映射是不完整的。下一行(记录类型 2 序列 2)应添加到先前映射的结果中。
我怎样才能用 lambda 解决这个问题而不必破坏线程安全?
最佳答案
目前使用 Stream API 不容易实现对匹配谓词的连续元素的操作。
一个选择是使用 StreamEx提供 groupRuns
的图书馆操作:
Returns a stream consisting of lists of elements of this stream where adjacent elements are grouped according to supplied predicate.
以下代码将连续行的记录部分号严格大于前一行的行组合在一起。记录号是用一个正则表达式提取的,该正则表达式查找第一个被忽略的数字之后的所有数字。
private static final Pattern PATTERN = Pattern.compile("\\d(\\d+)");
public static void main(String[] args) throws IOException {
try (StreamEx<String> stream = StreamEx.ofLines(Paths.get("..."))) {
List<Record> records =
stream.groupRuns((s1, s2) -> getRecordPart(s2) > getRecordPart(s1))
.map(RecordFactory::createRecord)
.toList();
}
}
private static final int getRecordPart(String str) {
Matcher matcher = PATTERN.matcher(str);
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
}
return 1; // if the pattern didn't find anything, it means the record is on a single line
}
这假设您的 RecordFactory
会创建一个 Record
来自List<String>
而不是来自 String
.请注意,此解决方案可以并行运行,但将文件内容存储到 List
中可能会更好。如果您想要更好的并行性能(以内存为代价),则对该列表进行后处理。
关于java - 依赖前一个和下一个元素的流处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35147904/