Un modo più elegante per evitare di codificare in modo rigido il formato di un file CSV?

3

So che questo è un problema banale, ma sento che questo può essere più elegante.

Quindi ho bisogno di scrivere / leggere i file di dati per il mio programma, diciamo che per il momento sono CSV. Posso implementare il formato come ritengo opportuno, ma potrei aver bisogno di cambiare quel formato in seguito. La semplice cosa da fare è qualcosa come

out.write(For.getValue()+","+bar.getMinValue()+","+fi.toString());

È facile da scrivere, ma ovviamente è colpevole di hard coding e del problema generale del "numero magico". Il formato è hardcoded, richiede l'analisi del codice per capire il formato del file e la modifica del formato richiede la modifica di più metodi.

Potrei invece avere le mie costanti che specificano la posizione in cui voglio che ciascuna variabile sia salvata nel file CSV per rimuovere alcuni dei "numeri magici"; quindi salva / carica in una matrice nella posizione specificata dalle costanti:

int FOO_LOCATION=0;
int BAR_MIN_VAL_LOCATION=1;
int FI_LOCATION=2
int NUM_ARGUMENTS=3;

String[] outputArguments=new String[NUM_ARGUMENTS];
outputArguments[FOO_LOCATION] = foo.getValue();
outputArgumetns[BAR_MIN_VAL_LOCATION] = bar.getMinValue();
outptArguments[FI_LOCATOIN==fi.toString();

writeAsCSV(outputArguments);

Ma questo è ... estremamente prolisso e ancora un po 'brutto. Rende facile vedere il formato del CSV esistente e scambiare facilmente la posizione delle variabili all'interno del file. Tuttavia, se decido di aggiungere un valore extra al csv, non devo solo aggiungere una nuova costante, ma anche modificare i metodi di lettura e scrittura per aggiungere la logica che salva / legge effettivamente l'argomento dalla matrice; Devo ancora scovare ogni metodo usando queste variabili e cambiarle a mano!

Se utilizzo le enumerazioni Java, posso tranquillamente ripulirlo, ma il vero problema è ancora presente. A parte qualche tipo di programmazione funzionale (e le classi interne di java sono troppo brutte per essere considerate funzionali) non ho ancora modo ovvio di esprimere chiaramente quale variabile è associata ad ogni costante di scrittura (e di mantenimento) nei metodi di lettura / scrittura . Ad esempio, devo ancora scrivere da qualche parte che FOO_LOCATION specifica la posizione di foo.getValue ().

Sembra che ci dovrebbe essere un modo più carino, più facile da mantenere, per avvicinarsi a questo?

Per inciso, sto lavorando in Java al momento, tuttavia, sono interessato concettualmente all'approccio progettuale indipendentemente dalla lingua. Alcune librerie in java che fanno tutto il lavoro per me sono sicuramente benvenute (anche se potrebbe rivelarsi più complicato ottenere il permesso di aggiungerlo alla base di codice, quindi semplicemente scrivere qualcosa a mano velocemente), ma quello che sto chiedendo è di più su come scrivere codice elegante se dovessi farlo a mano.

    
posta dsollen 30.10.2013 - 23:58
fonte

6 risposte

4

Indipendentemente dalla lingua, se non si desidera codificare i valori in modo rigido, è necessario disporre di una sorta di metadati che descriva il modo in cui i dati verranno mappati, formattati e stampati.

In questo esempio, potrebbe essere chiamato un mapper o formatter.

Mappare / formattare i file (XML, JSON o simliar) descriverà come i dati sarebbero stati formattati e scritti. La tua applicazione leggerà nel file mappa / formato e la userà per creare l'output.

Quindi la tua applicazione potrebbe formattare il CSV come meglio credi, senza un cambio di programmazione. Si potrebbe anche estenderlo a formati flat o fissi e XML. Quindi il tuo codice è generico poiché utilizza i metadati di mappatura per creare il file CSV.

Per il caso CSV, ad un livello elevato bisognerebbe descrivere:

  • Nome e ordine dei campi
  • Delimitatore (a volte la virgola non viene utilizzata)
  • Indica se includere o meno un'intestazione
  • Indica se includere o meno virgolette sui dati

Proprio come una nota a margine, c'è una differenza di tempo per sviluppare le due applicazioni. I valori del campo con hardcoded sono molto più veloci da sviluppare, anche se come hai sottolineato meno elegante. Ma se hai bisogno di fare qualcosa in fretta, l'aproach è OK.

Sviluppare qualcosa di più generico richiederebbe più tempo, ma se produci molti file in formato CSV di diverso formato, a lungo termine si otterrebbe il ROI su di esso.

Con questo approccio, si può anche scrivere una sorta di "GUI" per gli analisti di business da utilizzare per creare i file di mappa in modo che gli sviluppatori siano meno coinvolti nel processo complessivo.

    
risposta data 31.10.2013 - 16:25
fonte
1

I am interested conceptually about the design approach regardless of language

Per problemi di basso livello come questo, non esiste un "approccio progettuale a prescindere dalla lingua". Per Java, una risposta è usare le annotazioni per contrassegnare i campi che dovrebbero essere scritti nel CSV. Quindi una singola funzione di scrittura può utilizzare la riflessione per trovare i campi annotati e scriverli nel CSV. In alternativa, puoi lavorare con le strutture di serializzazione Java esistenti e scrivere tutti i campi che non sono transitori.

Per una comprensione più approfondita, leggi il codice per una libreria di serializzazione esistente. Potresti iniziare con uno semplice come openCSV .

    
risposta data 31.10.2013 - 01:34
fonte
0

Utilizza varargs ...

public void write(Object... args);

... e chiamalo così:

out.write(new Object { For.getValue(), bar.getMinValue(), fi.toString() });
    
risposta data 31.10.2013 - 00:39
fonte
0

Se non hai bisogno di csv, cerca di usare JAX-B per scriverli come XML invece di scriverli come CSV, se è per l'archiviazione di file locale. È ingombrante: annota solo le classi che conterranno le informazioni e usa un Marshaller. È quello che farei comunque. Nel caso ti interessi: link

D'altra parte, potresti avere un metodo generico per marshall in csv, come questo:

public String marshallCsv(String[][] data,boolean includeFirstLine){
    String delimeter = "\"";
    String separator = ",";
    StringBuilder csv = new StringBuilder("");
    int i =0;
    if(!includeFirstLine){
        i=1;
    }
    for(;i<data.length;i++){//if includeFirstLine is true, start from index 1
         for(j=0;j<data[i].length;j++){
             csv.append(delimeter+data[i][j]+delimeter);
             if(j<data[i].length){
                  csv.append(separator);
             }
             csv.append("\n");
         }
    }
    return csv.toString();
}

Se un determinato campo viene omesso, riempilo con una stringa vuota. È quindi possibile fare in modo che gli oggetti implementino un metodo, ad esempio "toContentStringArray ()" che restituisce i campi rilevanti come Stringhe di un array.

Per un controllo a grana fine, ci sono molte cose che puoi fare, ma mi piace il matto: puoi avere un sovraccarico di toContentStringArray che ha ricevuto una serie di stringhe che nominano i campi che vuoi produrre, quindi usa i riflessi per ottenere quei campi e ometti gli altri come stringhe vuote.

Per ottenere risultati migliori, assicurati di avere un sanitizeString (String cleanable, String [] forbiddenStrings), in modo che tu sfugga alle stringhe che non vuoi nel csv. Ad esempio, vuoi sfuggire a tutti i "," così i tuoi campi non saranno spezzati a metà.

Ma credimi, questo diventa complicato. Sicuramente andrei su JAX-B.

    
risposta data 31.10.2013 - 01:33
fonte
0

Etichetta i tuoi campi nella prima riga

Quando si tratta di scrivere i dati, il peccato capitale è presumere che tu e solo tu vorresti mai leggerlo. Un tale presupposto porta ad omettere la seconda parte più importante del tuo file, l'identificazione dei tuoi dati .

Puoi passare da CSV a formati auto-descrittivi come XML o JSON, oppure puoi semplicemente implementare la convenzione appropriata di includere un'etichetta per ogni campo nella prima riga. Con questa convenzione, diventa facile per il tuo motore di input mappare i dati in entrata a qualsiasi struttura interna sia appropriata, e il tuo motore di output è liberato dal dover prestare particolare attenzione all'ordine delle colonne.

I dettagli su come decidi quali campi includere variano in base alla lingua, ma questa è la programmazione.

    
risposta data 31.10.2013 - 14:01
fonte
0

Utilizza metadati.

In genere associo un file CSV con un file di risorse separato contenente la versione del formato e che elenca i campi. In questo modo puoi sviluppare la qualità nello stesso punto: controllando il numero di campi, i tipi di campo e tutti, in un pezzo generico di codice .

Ed è un po 'più dichiarativo come enum.

In effetti, a quel livello non si è interessati allo specifico trattamento commerciale dei dati, dando significati speciali ai campi. Quindi, perché creare specifiche lanterne di codifica. Non appena i dati arrivano sul lato business in DTOs o giù di lì, l'enum (ad esempio) ha il suo uso finale ed è ulteriormente morto: hai costruito due strati , dove ne sarebbe stato necessario uno solo da mantenere.

    
risposta data 31.10.2013 - 17:22
fonte

Leggi altre domande sui tag