将 CSV 文件中的值输入到 ArrayList 时出现问题

Problem inputting values from CSV file into ArrayList

提问人:herb 提问时间:10/16/2023 最后编辑:herb 更新时间:10/17/2023 访问量:67

问:

我正在尝试构建一个终端模式应用程序,允许用户输入某些属性作为 80 年代发布的丰田汽车的过滤搜索。这是一个类的作业,因此存在一些限制,例如将原始数据放在 CSV 文件中,并且必须将数据加载到对象的 ArrayList 中(在我的情况下是丰田汽车)。我决定使用 BufferedReader 来读取数据,并将 CSV 文件中的值设置为实例化新车辆并将它们添加到 ArrayList 的参数。

以下是实现我刚才解释的方法:

public void load() {
        toyotaVehicleCatalog = new ArrayList<>();
        try(BufferedReader br = new BufferedReader(new FileReader("./src/files/80stoyota.csv"))) {
            String line = "";
            while((line = br.readLine()) != null) {
                String[] fields = line.split(",");
                int modelYear = Integer.parseInt(fields[0]); //Line 28
                String modelName = fields[1];
                String modelCode = fields[2];
                String modelEngine = fields[3];
                boolean sedan = fields[4].length() > 0 ? true : false;
                String funFact = fields[5];

                ToyotaVehicle vehicle = new ToyotaVehicle(modelYear, modelName, modelCode, modelEngine, sedan,                   funFact);
                toyotaVehicleCatalog.add(vehicle);
            }

        } catch(IOException ex) {
            ex.printStackTrace();
        }
        
        for(int i = 0; i < toyotaVehicleCatalog.size(); i++) {
            System.out.println(toyotaVehicleCatalog.get(i));
        }

    } 

我的丰田车辆类:

public class ToyotaVehicle {
    private String modelCode;

    private int modelYear;

    private String modelName;

    private String engine;

    private boolean sedan;

    private String funFact;

    public ToyotaVehicle(int modelYear, String modelName, String modelCode, String engine, boolean sedan, String funFact) {
        this.modelCode = modelCode;
        this.modelYear = modelYear;
        this.modelName = modelName;
        this.engine = engine;
        this.sedan = sedan;
        this.funFact = funFact;
    }

    public ToyotaVehicle() {
    }

    public String getModelCode() {
        return modelCode;
    }

    public void setModelCode(String modelCode) {
        this.modelCode = modelCode;
    }

    public int getModelYear() {
        return modelYear;
    }

    public void setModelYear(int modelYear) {
        this.modelYear = modelYear;
    }

    public String getModelName() {
        return modelName;
    }

    public void setModelName(String modelName) {
        this.modelName = modelName;
    }

    public String getEngine() {
        return engine;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public boolean isSedan() {
        return sedan;
    }

    public void setSedan(boolean sedan) {
        this.sedan = sedan;
    }

    public String getFunFact() {
        return funFact;
    }

    public void setFunFact(String funFact) {
        this.funFact = funFact;
    }

}

当我运行代码时,我收到以下错误消息:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0 at data.LoadData.load(LoadData.java:28) at Driver.main(Driver.java:7)

如果我在这里似乎遗漏了一个明显的错误,我提前道歉。我唯一尝试过的是,起初我使用数组值作为直接参数,而不是为每个数组值设置变量,没有运气。我已经阅读了与同一问题相关的大多数论坛/帖子,但尚未找到解决方案。我以前使用过几次 BufferedReader,但从未遇到过这个问题。任何帮助将不胜感激,谢谢。files[]

Java ArrayList 缓冲读取器

评论

0赞 Old Dog Programmer 10/16/2023
对于此类问题,突出显示异常消息指向的源代码中的行会有所帮助。那么,“LoadData”的源文件中的第 28 行是哪一行?
0赞 Basil Bourque 10/16/2023
顺便说一句,您可以将整个车辆类别简化为单行:public record ToyotaVehicle( int modelYear, String modelName, String modelCode, String engine, boolean sedan, String funFact ) {}
0赞 herb 10/16/2023
@OldDogProgrammer 知道了,谢谢

答:

1赞 rzwitserloot 10/16/2023 #1

输入文件中有一个空行。可能在最后。这导致结果为零长度数组,从而在尝试时产生 .微不足道 通过在循环中检查这一点:.split(",")ArrayIndexOutOfBoundsExceptionfields[0]

while((line = br.readLine()) != null) {
  if (line.isEmpty()) continue; // add this
  String[] fields = line.split(",");
  int modelYear = Integer.parseInt(fields[0]);
  ....
}
1赞 Reilas 10/16/2023 #2

"...当我运行代码时,我收到以下错误消息:...”

当使用无效索引访问数组时,将引发 ArrayIndexOutOfBoundsException 类。
例如,如果您有 5 个值并尝试访问第 6 个值。

根据错误,它是从 load 方法抛出的。
加载中唯一的数组字段,因此这意味着其中一行 CSV 行的格式不正确。

这是解决该错误的方法。

首先,使用 −1 极限参数和 split 方法。
这会将尾随逗号保留为空值,例如“1,2,3,,,”。

String[] fields = line.split(",", -1);

跳过任何没有 6 个元素的行。
此外,创建一个 List 来保存格式不正确的行以进行调试。

List<String> list = new ArrayList<>();
String[] fields = line.split(",", -1);
if (fields.length != 6) {
    list.add(line);
    continue;
}

这是重构。

void load() {
    toyotaVehicleCatalog = new ArrayList<>();
    List<String> list = new ArrayList<>();
    try(BufferedReader br = new BufferedReader(new FileReader("./src/files/80stoyota.csv"))) {
        String line = "";
        while((line = br.readLine()) != null) {
            String[] fields = line.split(",", -1);
            if (fields.length != 6) {
                list.add(line);
                continue;
            }
            int modelYear = Integer.parseInt(fields[0]);
            String modelName = fields[1];
            String modelCode = fields[2];
            String modelEngine = fields[3];
            boolean sedan = fields[4].length() > 0 ? true : false;
            String funFact = fields[5];

            ToyotaVehicle vehicle = new ToyotaVehicle(modelYear, modelName, modelCode, modelEngine, sedan,                   funFact);
            toyotaVehicleCatalog.add(vehicle);
        }

    } catch(IOException ex) {
        ex.printStackTrace();
    }

    list.forEach(System.out::println);

    for(int i = 0; i < toyotaVehicleCatalog.size(); i++) {
        System.out.println(toyotaVehicleCatalog.get(i));
    }

}

以下是一些与此无关的其他建议。

或者,只需使用“src/files/80stoyota.csv”。
File 类推断相对路径。

使用 Scanner 类,而不是 BufferedReader

try(Scanner in = new Scanner(new File("src/files/80stoyota.csv")))

并且,使用记录类而不是

record ToyotaVehicle(int y, String n, String c, String e, boolean s, String f) { }

这是一个额外的重因。

void load() {
    toyotaVehicleCatalog = new ArrayList<>();
    List<String> list = new ArrayList<>();
    try(Scanner in = new Scanner("src/files/80stoyota.csv")) {
        String line = "", f[];
        while((line = in.nextLine()) != null) {
            f = line.split(",", -1);
            if (f.length != 6) {
                list.add(line);
                continue;
            }
            toyotaVehicleCatalog.add(
                new ToyotaVehicle(Integer.parseInt(f[0]), f[1], f[2], f[3], !f[4].isEmpty(), f[5]));
        }

    }

    list.forEach(System.out::println);

    toyotaVehicleCatalog.forEach(System.out::println);

}

编辑

"...我在数据中包含逗号,因此它正在拆分它并创建比应有的字段更多的字段。我尝试在包含逗号的字段周围使用双引号,但没有运气。..."

有几种方法可以解决此问题。

如果您已创建 CSV 文件,只需使用逗号以外的其他分隔符即可。
不需要逗号
Wikipedia – 分隔符分隔的值

如果没有,请尝试使用正则表达式模式进行解析。
这是一个相关的问题和答案。
StackOverflow – Java:拆分逗号分隔的字符串,但忽略引号中的逗号

或者,使用第三方库,我听说 Apache Commons 在这方面效果很好。
Apache Commons – CSV

评论

0赞 herb 10/16/2023
感谢您的输入。我尝试了您提供的第二次重构,但仍然出现错误。然后我尝试从 csv 文件切换到文本文件。这很有帮助,并表明每个值包含 6 个以上的字段(感谢调试帮助)。我在数据中包含逗号,因此它正在拆分它并创建比应有的字段更多的字段。我尝试在包含逗号的字段周围使用双引号,但没有运气。fields[]
0赞 Reilas 10/16/2023
@herb,你能从CSV文件中添加一行到你的问题吗?我不确定为什么它会失败。
0赞 herb 10/16/2023
这是文本文件中的一行:我确实将文件从.csv更改为.txt。1984, Toyota 4Runner, N60, "2.4 L I4", N, "The N60 was the first generation for the 4Runner. Toyota essentially added a removable fiberglass top to a Toyota pick-up truck. This concept helped set the stage for future off-road SUVs pf its kind."
0赞 Reilas 10/17/2023
@herb,好吧,我误会了你的意思。是的,如果引用的文本中有其他逗号,则会导致此处失败。我已经更新了我的问题,接近尾声。
0赞 herb 10/17/2023
感谢您的反馈和帮助@Reilas,非常感谢!