处理CSV文件中的重复标题名称

pls*_*shm 5 java csv duplicates

我使用一个模拟程序,在后台生成一个包含模拟数据的CSV文件(例如道路的长度,模拟的时间等)

文件外观的一个示例:

例

所以,在这种情况下,我知道只有一辆车(因为carId).现在,如果生成另一个CSV并且模拟包含2辆汽车,则列会发生变化:

两辆不同的车

因此,你可以看到添加了不同的列(其中很多都是无关紧要的,因为我只需要carId和mph).

如果有越来越多的汽车,列数增加,我永远不知道在哪个位置是mph的carId.

我想要的是为每个具有mph外观的carId创建一个CarObject.

在java中是否有一种方法可以为每个(carId,mph)列创建一个自己的表.所以我有例如.a List<CarDataRows>和CarDataRows类似于List,其中每个Item包含具有特定mph的carId.

编辑

在这种情况下,集合应包含两个项目.并且Item中的值也应该像包含具有不同英里/小时的carId的列表一样:

在此输入图像描述

myList.get(0)
-> 1,3
   1,4
   1,7


myList.get(1)
-> 2,12
   2,23
   2,0
Run Code Online (Sandbox Code Playgroud)

例如,在phyton中,您可以使用panda并创建foreach模式(carId with mph)和自己的数据帧.这就是我在这里要做的

Rob*_*ert 2

你的 CSV 看起来很奇怪,我不希望人们附加列,而是附加行,特别是当你有一个 ID 列来区分行时。

也就是说,我会将数据解析为 Car 对象,然后对其进行处理。这涉及三个步骤。

在文件的第一行,找到指示感兴趣的列的标题字段,此处carIdmph。Apache 的 CSV 库因重复的标头名称而阻塞(我不能真正责怪它),因此您需要找到一个更宽松的库或自己解析标头。

使用标题中的信息,您可以创建 Car 对象,并告诉它们它们的列。每辆车都负责自己的列号以及所有速度(您真正想要的信息)。

然后您阅读文件的其余部分,并将速度分配给他们的汽车。由于汽车拥有自己的数据(它们的列号和速度),因此您只需将每个记录字段传递给所有汽车,并要求它们根据自己的数据采取行动(告诉不要问原则)。

您的 Car 类可能如下所示:

import java.util.List;
import java.util.ArrayList;

public class Car {
    private int id;
    private final int idColumn;
    private final int speedColumn;
    private List<Integer> speeds;

    public Car(int idColumn, int speedColumn) {
        id = 0;
        this.idColumn = idColumn;
        this.speedColumn = speedColumn;
        speeds = new ArrayList<>();
    }

    public void add(int column, String value) {
        if (this.idColumn == column) {
            int i = Integer.parseInt(value);
            if (id != 0 && id != i) {
                throw new IllegalStateException("changed ID from "  + id + " to " + i);
            }
            id = i;
        }
        if (this.speedColumn == column) {
            speeds.add(Integer.parseInt(value));
        }
    }

    public List<Integer> getSpeeds() {
        return speeds;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用 Apache Common CSV 解析器像这样处理 CSV 输入:

import java.io.*;
import java.util.*;
import org.apache.commons.csv.*;

public class YourCarCsvParser {
    /** Column name for a car's ID. */
    private static String CAR_ID = "carId";

    public static void main(String[] args) throws Exception {
        App app = new App();
        for (String a : args) {
            app.parse(new InputStreamReader(new FileInputStream(a)));
        }
    }

    public List<Car> parse(Reader input) throws IOException {
        // Initialize the list so you'll get an empty list not null if there is no CSV data.
        List<Car> cars = new ArrayList<>(); 
        CSVParser parser = CSVParser.parse(input, CSVFormat.RFC4180);
        boolean hadHeaders = false;
        for (CSVRecord record : parser) {
            if (!hadHeaders) {
                cars = makeCars(record);
                hadHeaders = true;
            }
            else {
                addSpeedsToCars(cars, record);
            }
        }
        return cars;
    }

    private List<Car> makeCars(CSVRecord record) {
        List<Car> cars = new ArrayList<>();
        for (int i = 0; i < record.size(); i++) {
            String field = record.get(i);
            if (CAR_ID.equals(field)) {
                cars.add(new Car(i, speedColumnFor(i)));
            }
        }
        return cars;
    }

    private int speedColumnFor(int idColumn) {
        // Assumes speed is always right of car ID.
        return idColumn + 1;
    }

    private void addSpeedsToCars(List<Car> cars, CSVRecord record) {
        for (int i = 0; i < record.size(); i++) {
            for (Car c : cars) {
                 c.add(i, record.get(i));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这会将您的 CSV 转换为汽车列表,每辆汽车都有其速度列表。

该代码基于一些假设,但您可以根据需要轻松修复它们:

  • CSV 中的列名称区分大小写。如果不是,请在检查 时将 更改equals为。equalsIgnoreCaseCAR_ID

  • 速度列就在汽车 ID 列旁边:如果不是这种情况,请调整方法speedColumnFor以返回适当的列。当您没有固定偏移量但需要查看标题时,这会变得有点棘手,例如,在看到 ID 列后选择以下 mph 标题。在这种情况下,您必须调整逻辑makeCars以记住 ID 列位置,并且仅Car在遇到 mph 列时使用记住的 ID 列位置创建 。

  • 汽车 ID 是整数,就像您的示例数据中一样。您也可以将它们更改为字符串。

  • 速度是整数。如果需要浮动,请在从字符串转换时调整Integer.parseIntto Float.parseFloat