使用Streams按文件限制某个字符串限制行

时间:2016-11-17 06:06:24

标签: java file java-8 java-stream java.nio.file

我是Java 8 Stream API的新手,在以下情况下使用它时遇到了麻烦:

我必须逐行读取文件,并以其大小最接近某个字符限制的方式分组,然后将其发布到Kafka。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

void tester(char *s, int *cols, int *rows){
    int cnt;

    cnt = sscanf(s, "%d %d", cols, rows);
    if(cnt != 2 || *cols < 2 || *cols > 20 || *rows < 2 || *rows > 20){
        printf("Incorrect Matrix Dimensions!\n");
        exit(EXIT_FAILURE);//The program can not be continued.
    }
}

void allocateMem(char ***cell, int n, int m){
    *cell = malloc( n * sizeof(char*));
    for(int i = 0; i < n; i++)
        (*cell)[i] = malloc(m * sizeof(char));
}

int main(void){
    char buffer[200] = "";
    char **cell;
    int max_row, max_col;
    int i, j;

    fgets(buffer, sizeof buffer, stdin);//read first line
    tester(buffer, &max_col, &max_row);//If fgets fails, this also fails

    allocateMem(&cell, max_row, max_col);

    for(i = 0; i < max_row; ++i){
        for(j = 0; j < max_col; j++){
            int ch = fgetc(stdin);
            if(!isspace(ch))
                cell[i][j] = ch;
            else
                --j;//cancel this turn
        }
    }
    for (i = 0; i < max_row; i++){
        for (j = 0; j < max_col; j++){
            printf("%c", cell[i][j]);
        }
        puts("");//put newline
    }
    //deallocate cell
}

现在我完全习惯使用传统或声明风格,即逐行读取文件,使用循环组合它们并在大小最接近1024个字符时继续在kafka上发布消息。 但我想为此使用流。

注意:我正面临着这段代码的另一个问题,即 public void publishStringToKafka(File outputFile) { try { Files.lines(outputFile.toPath()) .forEach(s -> kafkaProducer.publishMessageOnTopic(s, KAFKA_INGESTION_TOPIC)); } catch (IOException e) { LOG.error("Could not read buffered file to send message on kafka.", e); } finally { try { Files.deleteIfExists(outputFile.toPath()); } catch (IOException e) { LOG.error("Problem in deleting the buffered file {}.", outputFile.getName(), e); } } } 命令在执行后不会删除文件,也不会发生异常。而如果我使用声明式样式,则文件将被成功删除。

请帮忙。

2 个答案:

答案 0 :(得分:2)

Collectors.groupingBy()在这种情况下很有用。

Map<T, List<String>> result = Files.lines(outputFile.toPath())
  .collect(Collectors.groupingBy(Your::classifier, Collectors.toList()))

结果,您得到Map<T,List<String>>T是Your :: classifier返回的类型。现在,您已经将所有内容分组,并且可以继续使用for-each。

现在,您可以提取条目集,对其进行排序,对其进行平面映射,然后发布到Kafka。 flatMap是必要的,因为如果你没有展平你的结构,你最终会迭代Stream<List<>>。这不一定是坏事,但我认为这不是理想的情况。

 collect.entrySet().stream()
   .sorted(Comparator.comparing(Map.Entry::getKey))
   .flatMap(e -> e.getValue().stream())
   .forEach(s -> kafkaProducer.publishMessageOnTopic(s, KAFKA_INGESTION_TOPIC));

唯一棘手的部分是适当地实施分类器方法,但从我明白你知道怎么做的问题。

答案 1 :(得分:2)

问题陈述您要做的是将流中的所有字符串按顺序组合到尽可能接近最大字符数量并创建新的List列表。然后,可以使用此新创建的列表流式传输到Kafka。这不是一个容易解决的问题,因为你必须处理状态。

<强>解决方案

使用Collector累积值

 List<String> result = someStrings.stream()
                                  .collect(ArrayList::new, (list, string) -> accumulate(list, string), List::addAll);

accumulate方法包含最大字符逻辑:

 private void accumulate(ArrayList<String> list, String string) {
        if (list.isEmpty() || list.get(list.size() -1).length() + string.length() > MAXIMUM_CHARACTERS){
            list.add(string);
        } else {
            list.set(list.size()-1, list.get(list.size()-1) + string);
        }
    }

如果您输入列表[as,1234,213,bd,de]且MAXIMUM_CHARACTERS设置为5,它将返回所需的输出[as,1234,213bd,de]。