2

我正在读取一个包含许多列 (22) 的文件,并且我正在使用 openCSV 来读取该文件。

每行都表示为一个字符串数组nextLine[]

我将不得不处理/验证列并且不想将它们称为数字(即nextLine[0]... nextLine[22]

我更愿意将它们称为nextLine[COLUMN_A] nextLine[COLUMN_B] ..etc.

我最初的方法是使用枚举创建一个单例

public enum Columns {
    INSTANCE;
    public int COLUMN_A = 0;  
    ....
    public int COLUMN_X = 22; 
}

然后我可以将数组称为:

nextLine[Columns.INSTANCE.COLUMN_A]

问题

这会是最好的方法吗?我只是怀疑,因为我有另一个模型类,它只是为所有列提供了 getter/setter,现在创建另一个类(单例)来将列表示为索引似乎是一项额外的工作。

对于上面的示例,如果我有一个模型类

public class Columns {
  private String columnA;
  public Columns (String columnA) {
    this.columnA = columnA;
  } 
  public void setColumnA(String columnA) {
    this.columnA = columnA;
  }
  public String getColumnA() {
    return this.columnA;
  }
} 

我可以以某种方式使用nextLine[columnA]而不是创建一个单例枚举类吗?

4

4 回答 4

2

枚举不是单例。单例是一种特定的软件设计模式,传统上并不认为枚举是这样的。至少可以说,您对 Enum 的使用很奇怪。如果您想使用枚举来引用特定列,您可以简单地执行以下操作:

public enum Column {
    public int index;

    A(0), B(1), C(2), D(3);

    public Column( int index ) {
        this.index = index;
    }
}

然后你就说:

String columnAValue = csv[row][Column.A.index]

这样做还允许您像这样遍历所有列:

for( Column column : Column.values ) {
    String column = csv[row][column.index];
}

您将无法使用您使用的模式来做到这一点,这使得使用 Enum 不值得。如果你要继续做你正在做的事情,只需在文件顶部将它们设为常规常量:

public class CsvParser {
    public static final int COLUMN_A = 0;
    public static final int COLUMN_B = 1;
    public static final int COLUMN_C = 2;

}

这与您使用的枚举方法没有区别,只是它更直接并且不涉及定义另一个枚举。

现在回答你的问题。这是最好的模式吗?以及所有架构类型的问题,这取决于。您是否正在构建一个必须对 CSV 的每一列进行特定验证的程序?也许 A 列是一个整数,必须用作整数,B 列是一个字符串,C 列是一个枚举,等等。或者,你必须为每一列附加特定的逻辑,那么是的,如果格式是这种模式是有意义的您的数据始终是可预测的。如果您必须支持多种数据格式,但它们是固定的(即只有 format1、format2、format3),那么您可以继续遵循这种模式。

但是,如果您必须读取任何类型的 csv 格式,但附加一些固定数量的解析和/或逻辑,那么您必须读取有关 csv 的一些元数据,以了解哪些列是数字,哪些是字符串等. 你的逻辑可以通过查看元数据来附加。当然,这更灵活,但定义元数据、读取元数据并处理它并非易事。如果您不需要此模型,请不要这样做。另一种模型的工作量要少得多,而且同样健壮。

如果你从大局来看这个。在第一个架构中,我们有元数据。它是我们在程序中创建的 Enum 或常量。所以元数据被内置到程序中。在第二种风格中,我们将元数据从程序中移出到外部表示中,这样它就不会被烘焙到程序中。用户可以在运行时更改元数据,而在第一个版本中无法更改元数据。

于 2012-10-06T15:22:19.353 回答
1

对于 22 列,我希望您不要单独处理列,而是以示意方式处理。因此,我会完全避免索引并做

for (String columnElement : nextLine) {
  // process columnElement
}

如果每一列都有单独的特定含义,那么数组(或 List 或 Map)也不是最佳设计。然后我宁愿使用枚举或类对每一行进行建模(或使用合适的框架而不是重新发明轮子)。我不会为每一行编写 (g,s)setters,而是使用多态性,以便枚举/类知道如何处理每一列。甚至更好:有一个类用于将处理委托给 Column 类型的对象,例如

class Line {
  List<Column> columns;

  public void processLine() {
    for (Column c: columns) {
      c.processColumn();
  }

}

class Column {
  public void processColumn() {
    ...
  }
}
于 2012-10-06T15:33:08.870 回答
0

我建议使用HashMap

在你的情况下可能是HashMap<String, String[]> map;

然后你可以得到你的价值观,比如:

String[] valuesIAmInterested =  map.get(columnA);

更好的是,您可以真正使用您的对象Column作为地图的键。

HashMap<Column, String[]>   map;
于 2012-10-06T15:18:50.903 回答
0

在考虑了更多之后,我认为这是你可以做的:

public class CsvParser {

    private File file;
    private boolean hasHeader = false;
    private Map<String,Integer> columnIndexes;
    private List<String[]> rows = new ArrayList<String[]>();

    public CsvParser( File file, boolean hasHeader ) throws IOException {
        this.file = file;
        this.hasHeader = hasHeader;
    }

    // use this to parse the header from the file
    private void parseColumns(LineNumberReader reader) {
        String line = reader.nextLine();
        if( line != null ) {
            String[] columns = line.split(",");
            setColumns( columns );
        }
    }

    // use this if there is no header in the data.
    public void setColumns( String[] columns ) {
        columnIndex = new HashMap<String,Integer>();
        for( int i = 0; i < columns.length; i++ ) columnIndexes.put( columns.trim(), i);
    }

    public void parse() throws IOException {
        LineNumberReader reader = new LineNumberReader( new FileReader( file ) );
        try {
           if( hasHeader ) parseColumns(reader);
           while( (line = reader.nextLine()) != null ) {
              rows.add( line.split(",") );
           }
        } finally {
           reader.close();
        }
    }

    public Collection<String> getColumns() {
       return columnIndexes.keys();
    }

    public int size() {
       return rows.size();
    }

    public int getInt( int row, String column ) {
       return Integer.parseInt(getString(row,column));
    }

    public String getString( int row, String column ) {
       return rows.get(row)[columnIndexes.get(column)];
    }

    public double getDouble( int row, String column ) {
       return Double.parseDouble(getString(row,column));
    }

    public float getFloat( int row, String column ) {
       return Float.parseFloat(getString(row,column));
    }
}
于 2012-10-06T15:46:36.877 回答