当前位置:  开发笔记 > 编程语言 > 正文

使用新的Java 8 Streams API解析唯一行的CSV文件

如何解决《使用新的Java8StreamsAPI解析唯一行的CSV文件》经验,为你挑选了2个好方法。

我想使用新的Java 8流API(对此我一个完整的新手)解析为一个特定的行中的CSV文件(其中以"内达"在名称列).使用以下文章获取动机,我修改并修复了一些错误,以便我可以解析包含3列的文件 - 'name','age'和'height'.

name,age,height
Marianne,12,61
Julie,13,73
Neda,14,66
Julia,15,62
Maryam,18,70

解析代码如下:

@Override
public void init() throws Exception {
    Map params = getParameters().getNamed();
    if (params.containsKey("csvfile")) {
        Path path = Paths.get(params.get("csvfile"));
        if (Files.exists(path)){
            // use the new java 8 streams api to read the CSV column headings
            Stream lines = Files.lines(path);
            List columns = lines
                .findFirst()
                .map((line) -> Arrays.asList(line.split(",")))
                .get();
            columns.forEach((l)->System.out.println(l));
            // find the relevant sections from the CSV file
            // we are only interested in the row with Neda's name
            int nameIndex = columns.indexOf("name");
            int ageIndex columns.indexOf("age");
            int heightIndex = columns.indexOf("height");
            // we need to know the index positions of the 
            // have to re-read the csv file to extract the values
            lines = Files.lines(path);
            List> values = lines
                .skip(1)
                .map((line) -> Arrays.asList(line.split(",")))
                .collect(Collectors.toList());
            values.forEach((l)->System.out.println(l));
        }
    }        
}

有没有办法避免在提取标题行后重新读取文件?虽然这是一个非常小的示例文件,但我将此逻辑应用于大型CSV文件.

是否有技术使用流API在提取的列名称(在文件的第一次扫描中)与剩余行中的值之间创建映射?

如何以形式返回一行List(而不是List>包含所有行).我更愿意只将行作为列名与其对应值之间的映射.(有点像JDBC中的结果集).我在这里看到了一个可能有用的Collectors.mapMerger函数,但我不知道如何使用它.



1> Holger..:

BufferedReader明确使用:

List columns;
List> values;
try(BufferedReader br=Files.newBufferedReader(path)) {
    String firstLine=br.readLine();
    if(firstLine==null) throw new IOException("empty file");
    columns=Arrays.asList(firstLine.split(","));
    values = br.lines()
        .map(line -> Arrays.asList(line.split(",")))
        .collect(Collectors.toList());
}

Files.lines(…)也适应BufferedReader.lines(…).唯一的区别是,Files.lines将配置流,以便关闭流将关闭读取器,我们在这里不需要,因为显式try(…)语句已经确保关闭BufferedReader.

请注意,在处理返回的流之后无法保证读取器的状态lines(),但我们可以执行流操作之前安全地读取行.



2> Tunaki..:

首先,您对此代码正在读取文件两次的担忧尚未建立.实际上,Files.lines返回一个懒惰填充的行的Stream.因此,代码的第一部分只读取第一行,代码的第二部分读取其余部分(它确实第二次读取第一行,即使被忽略).引用其文档:

从文件中读取所有行作为Stream.与readAllLines此不同,此方法不会将所有行读入a List,而是在流消耗时延迟填充.

关于返回一行的第二个问题.在函数式编程中,您要做的是称为过滤.Stream API借助于提供这样的方法Stream.filter.此方法采用Predicateas参数,该函数返回true应保留的所有项,false否则返回.

在这种情况下,我们希望在名称等于时Predicate返回.这可以写成lambda表达式.true"Neda"s -> s.equals("Neda")

因此,在代码的第二部分中,您可以:

lines = Files.lines(path);
List> values = lines
            .skip(1)
            .map(line -> Arrays.asList(line.split(",")))
            .filter(list -> list.get(0).equals("Neda")) // keep only items where the name is "Neda"
            .collect(Collectors.toList());

但请注意,这并不能确保名称中只有一个项目"Neda",它会将所有可能的项目收集到一个项目中List>.您可以添加一些逻辑来查找第一个项目,或者如果找不到任何项目则抛出异常,具体取决于您的业务需求.


请注意,在@ Holger的答案中Files.lines(path)直接使用BufferedReaderas 可以避免调用两次.


@ johnco3我同意Holger的意见,你应该开一个新问题.但是,为了给你一个提示,你可以从你得到的列表中创建地图(要创建地图,你需要遍历列表的元素,不要迭代文件的行;)).
推荐阅读
echo7111436
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有