Java使用POJO读取CSV后提取指定数据的问题求助
我帮你梳理代码里的几个关键问题,这些要么是导致编译失败的致命错误,要么就是统计结果为0的直接原因:
1. createFlights方法的编译错误
你在createFlights里调用Flights构造函数时,传入了一堆未定义的变量(比如dep_time、sched_dep_time这类)——这些变量根本没从metadata数组里解析出来,代码连编译都通不过,更别说运行统计了。如果这些字段是CSV里实际存在的,你得从metadata里读取并转换类型;如果暂时不需要这些字段,建议简化Flights的构造函数,只保留你需要的字段,或者给这些参数赋默认值(比如0、null)。
2. CSV列索引越界问题
你说CSV有19列表头,但代码里用了metadata[19]来获取time_hour——Java数组是从0开始索引的,19对应的是第20列,这会直接抛出ArrayIndexOutOfBoundsException,中断文件读取流程,导致fdata为空,统计结果自然是0。另外也要再核对origin的列索引是否正确(比如如果Origin是第14列,索引13是对的,但一定要和实际CSV表头顺序对应)。
3. 空值处理逻辑的缺陷
你用list.contains("NA")跳过空值行,但这个逻辑有两个大坑:
- 手动用
split(",")拆分CSV会破坏带引号的字段(比如字段内容包含逗号时),直接导致列索引完全混乱; contains("NA")会误判那些内容包含"NA"的有效字段(比如"NAXX"),错误跳过正常数据行。
建议要么逐个检查字段是否等于"NA",要么直接用成熟的CSV解析库(比如OpenCSV)来处理,避免手动拆分的各种边缘问题。
4. 字符串比较的潜在陷阱
如果CSV里的origin字段前后有空格(比如" EWR "),直接用equals("EWR")会返回false,导致统计不到目标数据。可以先调用trim()去除前后空格再做比较。
修正后的代码示例
我简化了代码,只保留核心逻辑,修复了上述问题:
import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; public class NycFlights13 { public static void main(String[] args) { NYC13data dataLoader = new NYC13data(); // 先打印总条目数,确认数据是否读取成功 System.out.println("总读取条目数:" + dataLoader.flightData.fdata.size()); int ewrCount = 0; for (Flight flight : dataLoader.flightData.fdata) { // 处理可能的null和空格问题 if (flight.getOrigin() != null && flight.getOrigin().trim().equals("EWR")) { ewrCount++; } } System.out.println("EWR出发的条目数:" + ewrCount); } } class NYC13data { FlightData flightData = new FlightData(); public NYC13data() { flightData.readFileFromCSV("C:\\Users\\boonl\\Desktop\\R Assignment\\flights.csv"); } } class FlightData { public List<Flight> fdata; public FlightData() { fdata = new ArrayList<>(); } public List<Flight> readFileFromCSV(String fileName) { Path pathToFile = Paths.get(fileName); try (BufferedReader br = Files.newBufferedReader(pathToFile, StandardCharsets.US_ASCII)) { // 跳过表头行 br.readLine(); String line; while ((line = br.readLine()) != null) { String[] variables = line.split(","); // 先检查列数是否符合预期 if (variables.length != 19) { System.out.println("跳过格式错误的行:" + line); continue; } // 逐个检查字段是否为NA boolean hasInvalidValue = false; for (String var : variables) { if ("NA".equals(var.trim())) { hasInvalidValue = true; break; } } if (hasInvalidValue) { continue; } try { Flight flight = createFlight(variables); fdata.add(flight); } catch (NumberFormatException e) { System.out.println("跳过数据类型错误的行:" + line); } } } catch (IOException ioe) { ioe.printStackTrace(); } return fdata; } private static Flight createFlight(String[] metadata) { // 假设Origin是第14列(索引13),请根据实际CSV表头调整 String origin = metadata[13].trim(); // 只保留需要的字段,其他字段可按需添加 return new Flight(origin); } } class Flight { private String origin; // 简化构造函数,只保留核心字段 public Flight(String origin) { this.origin = origin; } // 符合Java规范的驼峰命名getter public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } @Override public String toString() { return "Flight [origin=" + origin + "]"; } }
额外建议
- 强烈建议使用专业CSV解析库(比如OpenCSV),它能自动处理带引号的字段、换行、空值等各种复杂情况,比手动
split可靠得多; - 代码命名尽量符合Java规范(类名首字母大写、驼峰命名法),能大幅提升代码可读性;
- 可以添加更多调试输出(比如打印每条读取到的
origin值),方便排查数据是否被正确解析。
内容的提问来源于stack exchange,提问作者boonboon93

