动态选择 CSV 文件中的特定列

Selecting a particular Column in a CSV-file Dynamically

提问人:abdelsh 提问时间:4/24/2022 最后编辑:Alexander Ivanchenkoabdelsh 更新时间:4/28/2022 访问量:377

问:

我有这个CSV文件:

id,name,mark
20203923380,Lisa Hatfield,62
20200705173,Jessica Johnson,59
20205415333,Adam Harper,41
20203326467,Logan Nolan,77

我正在尝试使用以下代码处理它:

 try (Stream<String> stream = Files.lines(Paths.get(String.valueOf(csvPath)))) {
                DoubleSummaryStatistics statistics = stream
                        .map(s -> s.split(",")[index]).skip(1)
                        .mapToDouble(Double::valueOf)
                        .summaryStatistics();
} catch (IOException e) // more code

我想按名称获取该列

我想我需要验证索引是用户以整数形式输入的列的索引,如下所示:

int index = Arrays.stream(stream).indexOf(columnNS);

但这行不通。

流应具有以下值,例如:

列:"mark"

62, 59, 41, 77

Java CSV IO NIO

评论

0赞 D-Dᴙum 4/24/2022
我建议您考虑使用第三方库,例如 Apache commons-csv
0赞 abdelsh 4/24/2022
对于这个项目,我不能使用java SE库以外的任何库
0赞 g00se 4/24/2022
你知道列是什么,那么你为什么不建立一个这样你就可以做类似的事情呢?Map<String, Integer>.map(s -> s.split(",")[Mark.getColumn("id")])

答:

1赞 Alexander Ivanchenko 4/24/2022 #1

我需要验证索引是用户以整数形式输入的列的索引...但这行不通。

Arrays.stream(stream).indexOf(columnNS)

Stream IPA 中没有方法。我不确定你的意思,但这种方法是错误的。indexOfstream(stream)

为了获取有效的索引,您需要列的名称。根据名称,您必须分析从文件中检索到的第一行。就像在列名“mark”的示例中一样,您需要找出此名称是否存在于第一行中以及它的索引是什么。

我想要的是按它的名字获取该列......溪流应该......

流应该是有状态的。它们是在 Java 中引入的,以便提供富有表现力和清晰的代码结构化方式。即使你设法将有状态条件逻辑塞进一个流中,你也会失去这个优势,最终得到的复杂代码不如普通循环那么清晰(其余部分:迭代解决方案几乎总是表现得更好)。

因此,要保持代码整洁,可以选择:使用迭代方法解决此问题,或者放弃在流中动态确定列索引的要求。

这就是如何使用循环来解决基于列名动态读取文件数据的任务:

public static List<String> readFile(Path path, String columnName) {
    List<String> result = new ArrayList<>();
    try(var reader = Files.newBufferedReader(path)) {
        int index = -1;
        String line;
        while ((line = reader.readLine()) != null) {
            String[] arr = line.split("\\p{Punct}");
            if (index == -1) {
                index = getIndex(arr, columnName);
                continue; // skipping the first line
            }
            result.add(arr[index]);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return result;
}
// validation logic resides here
public static int getIndex(String[] arr, String columnName) {
    int index = Arrays.asList(arr).indexOf(columnName);
    if (index == -1) {
        throw new IllegalArgumentException("Given column name '" + columnName + "' wasn't found");
    }
    return index;
}
// extracting statistics from the file data
public static DoubleSummaryStatistics getStat(List<String> list) {
    return list.stream()
        .mapToDouble(Double::parseDouble)
        .summaryStatistics();
}

public static void main(String[] args) {
    DoubleSummaryStatistics stat = getStat(readFile(Path.of("test.txt"), "mark"));
}

评论

0赞 abdelsh 4/25/2022
我不想获取 main 方法上的值,我想获取另一个方法的值,那么我如何使用 ''' DoubleSummaryStatistics stat = getStat(readFile(csvPath, columnNs));''' 与 getter 和 setters?.getMin()getMax()getAverage()//code public static DoubleSummaryStatistics getStat(List<String> list) { return list.stream() .mapToDouble(Double::valueOf) .summaryStatistics(); } public void setMean(BigDecimal mean) { this.mean = mean; } //more setters
0赞 Alexander Ivanchenko 4/25/2022
@abdelsh 如果要基于 的数据构造自定义对象,您可以创建一个单独的方法负责此。我建议使用构造函数,使用 Builder 模式,而不是 setter。如果您在实现此逻辑时遇到问题,因为它与从文件中读取数据的主题没有直接关系,我建议您打开一个新问题来解决这个问题。DoubleSummaryStatistics
0赞 abdelsh 4/25/2022
效果非常好,但是现在我在计算方面遇到了问题,那么如何对流进行排序,获取它的中间值,或者如果有两个中间值,则获取它们并获取其中位数的最佳方法
0赞 Alexander Ivanchenko 4/25/2022
@abdelsh (docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/...) 可以为您提供平均值、最小值和最大值。要培养中位数,您可以使用如下返回的列表创建一个数组: - 这将给出一个排序的数组。下一步请参阅此处或本教程DoubleSummaryStatisticsdouble[]readFile()readFile().stream().mapToDouble(Double::valueOf).sorted().toArray();
0赞 abdelsh 4/26/2022
如何实现 writeFile 方法来创建新文件并实现它们?我正在做的是尝试在 fileReader 中创建文件,这是下一个代码String[] arr = String.valueOf(csvPath).split("\\\\"); int i = 0; for (i=0; i< arr.length; i++){ } String csvFile = arr[i]; File newFile = new File(csvFile); try { newFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); }