2012-10-06 4 views
2

Je lis un fichier qui contient beaucoup de colonnes (22) et j'utilise openCSV pour lire le fichier.La meilleure façon d'éviter les nombres magiques dans mon cas

chaque ligne est représenté sous la forme d'un tableau de chaînes nextLine[]

je vais devoir traiter/valider les colonnes et ne veulent pas se référer à eux comme nombre (c.-à-nextLine[0] ... nextLine[22])

I préféreraient les référer comme nextLine[COLUMN_A] nextLine[COLUMN_B] ..etc.

Mon approche initiale est de créer un singleton à l'aide d'un ENUM

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

alors je peut se référer au tableau comme:

nextLine[Columns.INSTANCE.COLUMN_A] 

Question

Serait-ce une meilleure approche? Je doute seulement parce que j'ai une autre classe de modèle qui a simplement des getters/setters pour toutes les colonnes, et maintenant créer une autre classe (singleton) pour représenter les colonnes en tant qu'index semble être un effort supplémentaire.

Pour l'exemple ci-dessus, si j'avais une classe de modèle comme

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

Puis-je utiliser en quelque sorte nextLine[columnA] plutôt que de créer une classe singleton ENUM?

+0

+1 Question Excellent, Je cherche aussi une bonne façon de le faire. – Robin

Répondre

2

Une énumération n'est pas un singleton btw. Un singleton est un modèle de conception de logiciel spécifique dont les enum ne sont pas traditionnellement considérés comme tels. Votre utilisation de l'Enum est étrange, c'est le moins qu'on puisse dire. Si vous souhaitez utiliser un ENUM pour se référer à des colonnes spécifiques que vous pouvez simplement effectuer les opérations suivantes:

public enum Column { 
    public int index; 

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

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

Ensuite, vous dites simplement:

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

Faire de cette façon vous permet également de parcourir l'ensemble des les colonnes comme ceci:

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

vous ne seriez pas en mesure de le faire en utilisant le modèle que vous avez utilisé ce qui rend l'utilisation du Enum pas la peine.Si vous allez continuer à faire ce que vous faisiez simplement les rendre constantes régulières en haut du fichier:

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

} 

Il n'y a pas de différence entre cela et l'approche ENUM que vous avez utilisé, sauf qu'il est plus direct et doesn n'implique pas de définir encore une autre énumération.

Maintenant à votre question. Est-ce le meilleur modèle à suivre? Eh bien comme toutes les questions de type d'architecture cela dépend. Vous construisez un programme qui doit effectuer une validation spécifique sur chaque colonne du CSV? Peut-être que la colonne A est un entier et doit être utilisée comme un entier, et la colonne B est une chaîne, et la colonne C est un Enum, etc. Ou, vous devez attacher une logique spécifique à chaque colonne alors oui ce modèle a du sens si le format de vos données est toujours prévisible. Si vous devez prendre en charge plusieurs formats de données, mais qu'ils sont corrigés (c'est-à-dire uniquement format1, format2, format3), vous pouvez continuer à suivre ce modèle. Cependant, si vous devez lire n'importe quel type de format csv, mais attacher une certaine quantité d'analyse et/ou de logique, alors vous devrez lire des métadonnées sur votre csv pour savoir quelles colonnes sont des nombres, sont des chaînes, etc. Et votre logique pourrait être attachée en regardant les métadonnées. Bien sûr, cela est plus flexible, mais la définition des métadonnées et la lecture de celles-ci et leur traitement sont non-triviales. Si vous n'avez pas besoin de ce modèle, ne le faites pas. L'autre modèle est beaucoup moins de travail et tout aussi robuste.

Si vous regardez cela à partir de la grande image. Dans la première architecture, nous avons des métadonnées. C'est l'Enum ou les constantes que nous avons créées dans le programme. Ainsi, les métadonnées sont intégrées dans le programme. Dans le deuxième style, nous avons déplacé les métadonnées du programme vers une représentation externe afin qu'elles ne soient pas intégrées au programme. L'utilisateur peut modifier les métadonnées au moment de l'exécution, où les métadonnées ne peuvent pas être modifiées dans la première version.

+1

C'est comme construire votre propre implémentation 'ResultSet'. –

+0

Seulement si vous avez besoin de métadonnées externalisées. Certains programmes ont besoin d'analyser n'importe quel type de csv et ils doivent connaître le nombre par rapport à la chaîne par rapport aux valeurs cardinales et valider ces données. Beaucoup d'autres ne le font pas. Cela dépend donc du style de programme que vous avez quant à l'architecture dont vous avez besoin. – chubbsondubs

0

Je suggère d'utiliser un HashMap

Dans votre cas pourrait être HashMap<String, String[]> map;

Ensuite, vous pouvez obtenir vos valeurs comme:

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

Mieux encore, vous pouvez vraiment utiliser votre objet Column comme clé pour votre carte.

HashMap<Column, String[]> map; 
+1

Je suppose que cela pourrait être 'Liste > data' –

+0

Si votre fichier CSV contient un en-tête, vous pouvez analyser cet en-tête pour connaître les noms des colonnes et les placer dans une Carte comme ceci avec l'index vous les avez trouvés, mais si vous devez ensuite assigner une logique spécifique à chacun d'eux, vous ne pouvez pas changer le format du fichier CSV. C'est comme une demi-métadonnée. Vous pouvez échanger la position de colonne, mais ces colonnes doivent toujours être présentes. Vous pourriez être en mesure d'avoir des colonnes obligatoires ou facultatives. – chubbsondubs

1

Avec 22 colonnes, je m'attends à ne pas traiter les colonnes individuellement mais plutôt de manière schématique. Par conséquent, je voudrais éviter indices tout à fait et faire

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

Si chaque colonne a une personne, une signification spécifique, un tableau (ou liste ou carte) n'est pas la meilleure conception, que ce soit. Ensuite, je préfère aller avec un enum ou une classe de modélisation de chaque ligne (or use a suitable framework instead of reinventing the wheel). Je ne voudrais pas écrire (g, s) etters pour chaque ligne, mais plutôt utiliser le polymorphisme de sorte que l'enum/class sache comment traiter chaque colonne. Ou encore mieux: avoir une classe pour une ligne qui délègue le traitement à des objets de type Colonne, par ex.

class Line { 
    List<Column> columns; 

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

} 

class Column { 
    public void processColumn() { 
    ... 
    } 
} 
0

Après avoir réfléchi un peu plus je pense que c'est ce que vous pourriez faire:

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)); 
    } 
} 
Questions connexes