2016-03-31 35 views
9

Voglio leggere un file csv contenente milioni di righe e utilizzare gli attributi per la mia decisione Albero algoritmo. Il mio codice è qui sotto:Leggere un file csv con milioni di righe via java il più velocemente possibile

String csvFile = "myfile.csv"; 
List<String[]> rowList = new ArrayList(); 
String line = ""; 
String cvsSplitBy = ","; 
String encoding = "UTF-8"; 
BufferedReader br2 = null; 
try { 
    int counterRow = 0; 
    br2 = new BufferedReader(new InputStreamReader(new FileInputStream(csvFile), encoding)); 
    while ((line = br2.readLine()) != null) { 
     line=line.replaceAll(",,", ",NA,"); 
     String[] object = line.split(cvsSplitBy); 
     rowList.add(object); 
     counterRow++; 
    } 
    System.out.println("counterRow is: "+counterRow); 
    for(int i=1;i<rowList.size();i++){ 
     try{ 
      //this method includes many if elses only. 
      ImplementDecisionTreeRulesFor2012(rowList.get(i)[0],rowList.get(i)[1],rowList.get(i)[2],rowList.get(i)[3],rowList.get(i)[4],rowList.get(i)[5],rowList.get(i)[6]); 
     } 
     catch(Exception ex){ 
      System.out.printlnt("Exception occurred"); 
     } 
    } 
} 
catch(Exception ex){ 
    System.out.println("fix"+ex); 
} 

Sta funzionando bene quando la dimensione del file csv non è grande. Tuttavia, è davvero grande. Quindi ho bisogno di un altro modo per leggere un csv più veloce. C'è qualche consiglio? Apprezzato, grazie.

+1

Si utilizzano due regex nascoste in una riga ('replaceAll' e' split'). Dato che il tuo CSV è così semplice, ti suggerisco di utilizzare uno splitter, come [Splash di Guava] (http://google.github.io/guava/releases/19.0/api/docs/com/google/common/base/ Splitter.html), che fa il lavoro proprio come previsto (a differenza di 'split' che ha un sacco di problemi). Inoltre, nonostante sia semplice, come puoi essere sicuro che il tuo metodo 'ImplementDecisionTreeRulesFor2012' non sia la causa principale della tua lentezza? –

+1

Forse una libreria può soddisfare le tue esigenze. Vedo che c'è un confronto qui: https://github.com/uniVocity/csv-parsers-comparison – Akah

+1

Perché hai bisogno di 2 loop e 'rowList' per supportarli? Perché non avere un singolo loop che legge in una riga e lo passa a 'ImplementDecisionTreeRulesFor2012'? –

risposta

5

In questo frammento vedo due questioni che vi rallenterà considerevolmente:

while ((line = br2.readLine()) != null) { 
    line=line.replaceAll(",,", ",NA,"); 
    String[] object = line.split(cvsSplitBy); 
    rowList.add(object); 
    counterRow++; 
} 

In primo luogo, rowList inizia con la capacità di default e dovrà essere aumentata molte volte, causando sempre una copia del vecchio sottostante array al nuovo.

Peggio, tuttavia, è l'eccessiva esplosione dei dati in un oggetto String []. Avrai bisogno delle colonne/celle solo quando chiami ImplementDecisionTreeRulesFor2012 per quella riga - non sempre mentre leggi quel file ed elabori tutte le altre righe. Sposta la divisione (o qualcosa di meglio, come suggerito dai commenti) nella seconda riga.

(creazione di molti oggetti è male, anche se ci si può permettere la memoria.)

Forse sarebbe meglio chiamare ImplementDecisionTreeRulesFor2012 mentre leggete i "milioni"? Eviterebbe del tutto l'elenco delle righe di ArrayList.

seguito Il rinvio della scissione riduce il tempo di esecuzione di 10 milioni di righe da 1m8.262s (quando il programma ha esaurito lo spazio heap) a 13.067s.

Se non si è obbligati a leggere tutte le righe prima di poter chiamare Implp ... 2012, il tempo si riduce a 4.902 s.

Infine crei la scissione e sostituire manualmente:

String[] object = new String[7]; 
//...read... 
    String x = line + ","; 
    int iPos = 0; 
    int iStr = 0; 
    int iNext = -1; 
    while((iNext = x.indexOf(',', iPos)) != -1 && iStr < 7){ 
     if(iNext == iPos){ 
      object[iStr++] = "NA"; 
     } else { 
      object[iStr++] = x.substring(iPos, iNext); 
     } 
     iPos = iNext + 1; 
    } 
    // add more "NA" if rows can have less than 7 cells 

riduce il tempo di 1.983s. Questo è circa 30 volte più veloce del codice originale, che viene comunque eseguito in OutOfMemory.

+0

Come hai fatto i tuoi orari? Se stai leggendo dal disco ci sono buone probabilità che dopo la prima esecuzione il file possa essere nella cache dei file nell'OS – tddmonkey

+0

Ho dimenticato di aggiungere che il * codice * originale ha esaurito lo spazio heap dopo più di un minuto (vedi modifica). Questo non è un problema del tempo richiesto per leggere un file. – laune

+0

Ne sei completamente sicuro? Probabilmente vorrei svuotare la cache tra le esecuzioni solo per essere assolutamente sicuro – tddmonkey

2

Basta usare il parser CSV uniVocity-parsers invece di provare a creare il parser personalizzato. La tua implementazione probabilmente non sarà abbastanza veloce o flessibile da gestire tutti i casi d'angolo.

È estremamente efficiente in termini di memoria ed è possibile analizzare un milione di righe in meno di un secondo.

Ecco un semplice esempio di come usarlo:

CsvParserSettings settings = new CsvParserSettings(); // you'll find many options here, check the tutorial. 
CsvParser parser = new CsvParser(settings); 

// parses all rows in one go (you should probably use a RowProcessor or iterate row by row if there are many rows) 
List<String[]> allRows = parser.parseAll(new File("/path/to/your.csv")); 

MA, che carica tutto in memoria.Per eseguire lo streaming tutte le righe, si può fare questo:

String[] row; 
parser.beginParsing(csvFile) 
while ((row = parser.parseNext()) != null) { 
    //process row here. 
} 

L'approccio più veloce è quello di utilizzare un RowProcessor, dà anche una maggiore flessibilità:

settings.setRowProcessor(myChosenRowProcessor); 
CsvParser parser = new CsvParser(settings); 
parser.parse(csvFile); 

Infine, si è dotato di routines che utilizzano il parser per eseguire alcune attività comuni (iterare java bean, dump ResultSet s, ecc.)

Questo dovrebbe coprire le nozioni di base, controllare la documentazione per trovare l'approccio migliore per il tuo caso.

Disclosure: Sono l'autore di questa biblioteca. È open source e gratuito (licenza Apache V2.0).

+0

Ho provato a utilizzare questo e ci vuole molto tempo per caricare il file CSV. – sskumar86

+0

Il metodo 'parseAll' carica tutto in memoria. In realtà dovresti usare 'beginParsing', quindi' parseNext' per iterare sui risultati, o semplicemente definire un 'RowProcessor', quindi chiamare' parse'. Questo NON caricherà tutto in memoria. Aggiornerò la mia risposta per mostrare un esempio –

+0

aumentando l'heap, l'univocità funziona bene. Tuttavia, non vedo univocità molto utile mentre cerchiamo di analizzare più file csv contemporaneamente. Mi raccomando se ho sbagliato qui. – sskumar86

Problemi correlati