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]

質問

これは最善のアプローチでしょうか?すべての列に対して単純にゲッター/セッターを持つ別のモデルクラスがあり、列をインデックスとして表す別のクラス (シングルトン) を作成することは、追加の労力のように思われるため、疑問に思うだけです。

上記の例では、次のようなモデル クラスがあるとします。

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];
}

使用したパターンを使用してそれを行うことはできないため、列挙型を使用する価値はありません。あなたがやっていることを続けるつもりなら、ファイルの先頭でそれらを通常の定数にします:

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 は Enum などです。または、各列に特定のロジックをアタッチする必要がある場合、このパターンはフォーマットが常に予測可能です。複数のデータ形式をサポートする必要があるが、それらが固定されている (つまり、format1、format2、format3 のみ) 場合は、引き続きこのパターンに従うことができます。

ただし、任意のタイプのcsv形式を読み取る必要があるが、固定量の解析やロジックをそれに添付する必要がある場合は、csvに関するメタデータを読み取って、どの列が数値で、どの列が文字列であるかなどを知る必要があります。 . また、メタデータを参照することで、ロジックを関連付けることができます。もちろん、これはより柔軟ですが、メタデータを定義し、それを読み取って処理することは簡単ではありません。このモデルが必要ない場合は、実行しないでください。もう 1 つのモデルは作業がはるかに少なく、堅牢です。

これを大枠で見ると。最初のアーキテクチャにはメタデータがあります。これは、プログラムで作成した Enum または定数です。したがって、メタデータはプログラムに組み込まれています。2 番目のスタイルでは、メタデータをプログラムから外部表現に移動したため、プログラムに組み込まれません。ユーザーは実行時にメタデータを変更できますが、最初のバージョンではメタデータを変更できません。

于 2012-10-06T15:22:19.353 に答える
1

22 列の場合、列を個別に処理するのではなく、概略的に処理することを期待しています。したがって、私はインデックスを完全に避けて、

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

各列に個別の特定の意味がある場合、配列 (またはリストまたはマップ) も最適な設計ではありません。次に、各行をモデル化する列挙型またはクラスを使用します (または、車輪を再発明する代わりに適切なフレームワークを使用します)。各行に (g,s)etters を記述するのではなく、列挙型/クラスが各列の処理方法を認識できるようにポリモーフィズムを使用します。またはさらに良い: 処理を列型のオブジェクトに委譲する行のクラスを用意します。

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

もう少し考えた後、これがあなたにできることだと思います:

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 に答える
0

を使用することをお勧めしますHashMap

あなたの場合はHashMap<String, String[]> map;

次に、次のような値を取得できます。

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

さらに良いことに、実際にオブジェクトColumnをマップのキーとして使用できます。

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