使用Java解析CSV时管理文件列名称和位置

时间:2012-04-03 12:27:42

标签: java csv calculated-columns file-processing

我正在编写一些java代码,它应该解析具有不同列类型和值的csv文件。基本文件看起来像这样(CSV),没有标题/列行。为了使处理文件时更简单,我希望能够使用列名访问每个单元格的索引值。 目前我不想使用CSV解析器

    Column1 | Column2 | Column3 |...
    --------+---------+---------+---
    val10   | val20   | val30   |
    val11   | val21   | val31   |
    val12   | val22   | val32   |
    ...     | ...     | ...     |

我考虑过使用列名的ArrayList(按顺序),因为枚举不像C ++那样转换回整数。这样我可以做类似的事情:

    ArrayList<String> columnNames = new ArrayList<String>();
    columnNames.add("Column1");
    columnNames.add("Column2");
    columnNames.add("Column3");

    // read each line from the file ...
    String[] row = line.trim().split(",");
    String col2 = row[ columnNames.indexOf("Column2") ];

我是Java的新手 - 有更好/更聪明的方法吗?感谢。

3 个答案:

答案 0 :(得分:2)

您的代码有效。但是,如果你正在寻找“更好”的方式,你可能需要重新考虑两点:

  1. List的indexOf(object)方法不是那么快。费用O(n)。如果你保持Map<columnNameString, indexNumber>,并从colName获取索引,它应该比你当前的impl更快。除此之外,在java中,您可以从枚举中获得不同类型的值。你甚至可以让你的枚举实现接口。

  2. 你应该做一些异常处理。如果文件中的一行缺少(或更多)列,该怎么办?您当前的代码将抛出OutOfbound异常。但是我希望这已经在您的真实代码中完成了。

答案 1 :(得分:1)

解决此问题的最简单方法是使用集合库并创建一个地图列表,其中地图中的键是列名,如下所示:

List<Map<String,String>> records = someCodeForReadingDataFromFile();

将每一行拆分为数组,然后创建值的映射:

List<Map<String,String>> someCodeForReadingDataFromFile() {
  List<<Map<String,String>> rowsList = new LinkedList<<Map<String,String>>();
  final String[] columnNames = {"Column1", "Column2", "Column3"};

  // add some loop to read one line at the time from the file
  ...
  String[] rows = line.trim().split(",");
  Map<String, String> rowMap = new HashMap<String, String>();
  for(int columnIndex = 0; columnIndex < columnNames.length; columnIndex++) {
     rowMap.put(columnNames[columnIndex], rows[columnIndex]); 
  }
  rowsList.add(rowMap);
  // repeat this until you reach EOF
  return rowsList;
}

然后,您可以访问CSV文件中的所有单元格的行索引和列名称:

String valueOne = records.get(0).get("Column1"); // will set the value to "val10"

如果列名已修复,您仍然可以创建一个枚举,例如

public enum Columns {
 Column1, Column2;
}

然后使用从Enum类继承的name()方法来获取值:

String valueOne = records.get(0).get(Columns.Column1);

但是,如果你决定使用库来简化这个过程,我真的可以推荐Smooks library甚至Apache Commons CSV(非常轻量级!)。

答案 2 :(得分:1)

你的一个断言是不准确的。你声明“枚举不会像在C ++中那样转换回整数”,这是真的。但是,Java中的Enums实际上比这更灵活!它们是对象,您可以拥有任意数量的值或属性,而不仅仅是数字。考虑这个(未经测试的)代码:

public enum ColumnEnum {
    COL1(1),
    COL2(2),
    COL3(3);

    private final int index;
    ColumnEnum(int index) {
        this.index = index;
    }
    public double index()   { return index; }
}

现在您可以像这样引用数组的部分:

// read each line from the file ...            
String[] row = line.trim().split(",");            
String col2 = row[ ColumnEnum.COL1.index() ];