2013-04-06 12 views
10

Ho un file di testo (XML creato con XStream) che è lungo 63000 righe (3,5 MB). Sto cercando di leggerlo usando il lettore Buffered:Il file di testo lungo in lettura Java è molto lento

   BufferedReader br = new BufferedReader(new FileReader(file)); 
       try { 
        String s = ""; 
        String tempString; 
        int i = 0; 
        while ((tempString = br.readLine()) != null) { 
         s = s.concat(tempString); 
//      s=s+tempString; 
         i = i + 1; 
         if (i % 1000 == 0) { 
          System.out.println(Integer.toString(i)); 
         } 
        } 
        br.close(); 

Qui puoi vedere i miei tentativi di misurare la velocità di lettura. Ed è molto basso. Ci vogliono secondi per leggere 1000 righe dopo 10000 linee. Sto chiaramente facendo qualcosa di sbagliato, ma non riesco a capire cosa. Grazie in anticipo per il vostro aiuto.

+0

L'intento è di analizzare questo file? Perché non caricarlo con Xerces/SAX/altri strumenti di analisi? –

+10

La stringa '+' e 'concat' è molto inefficiente se le stringhe sono grandi. Usa 'StringBuilder' o passa' InputStream'/'Reader' direttamente al parser xml. –

+0

Oppure se hai veramente bisogno di linee, usa qualcosa di simile - http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#readLines%28java .io.Reader% 29. –

risposta

4

@PaulGrime ha ragione. Stai copiando la stringa ogni volta che il loop legge una linea. Una volta che la stringa diventa grande (diciamo grande 10.000 righe), sta facendo un sacco di lavoro per fare quella copia.

Prova questa:

StringBuilder sb = new StringBuilder(); 
while (...reading lines..){ 
    .... 
    sb.append(tempString); //should add newline 
    ... 
} 

s = sb.toString(); 

Nota: leggere la risposta di Paolo di seguito sul perché strippaggio a capo rende questo un brutto modo di leggere in un file. Inoltre, come menzionato nei commenti delle domande, XStream fornisce un modo per leggere il file e anche se non lo fosse, IOUtils.toString (reader) sarebbe un modo più sicuro di leggere un file.

+0

Grazie! Davvero accelerato il caricamento. – lozga

+1

-1 la penalizzazione delle prestazioni non è la copia, Stringbuilder è quello consigliato nella documentazione, 'PaulGrime ha ragione' non è davvero una risposta che merita di essere accettata ... e 10000? perché? – UmNyobe

+0

Ho detto "diciamo 10.000" che significa "ad esempio, quando 10.000 linee sono grandi". Ho anche spiegato perché Paul aveva ragione e ha dato un esempio di codice. Inoltre, ti preghiamo di chiarire cosa intendi con "non solo copiare". –

4

Alcuni miglioramenti immediati che si possono fare:

  • utilizzare un StringBuilder invece di concat e +. L'utilizzo di + e concat può davvero influire sulle prestazioni, specialmente se utilizzato nei loop.
  • Ridurre l'accesso al disco. È possibile farlo utilizzando un large buffer:

    BufferedReader br = nuovo BufferedReader (nuovo FileReader ("someFile.txt"), SIZE);

1

Si dovrebbe utilizzare un StringBuilder come String concatenazione è estremamente lento per anche piccole stringhe.

Inoltre, provare a utilizzare NIO anziché BufferedReader.

public static void main(String[] args) throws IOException { 
    final File file = //some file 
    try (final FileChannel fileChannel = new RandomAccessFile(file, "r").getChannel()) { 
     final StringBuilder stringBuilder = new StringBuilder(); 
     final ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 
     final CharsetDecoder charsetDecoder = Charset.forName("UTF-8").newDecoder(); 
     while (fileChannel.read(byteBuffer) > 0) { 
      byteBuffer.flip(); 
      stringBuilder.append(charsetDecoder.decode(byteBuffer)); 
      byteBuffer.clear(); 
     } 
    } 
} 

È possibile ottimizzare la dimensione del buffer se è ancora troppo lento - è fortemente dipendente dal sistema che dimensione del buffer funziona meglio. Per me fa davvero poca differenza se il buffer è 1K o 4K, ma su altri sistemi so che cambi per aumentare la velocità di un ordine di grandezza.

1

In aggiunta a ciò che è stato già detto, in base all'utilizzo dell'XML, il codice è potenzialmente errato in quanto elimina le terminazioni di riga. Ad esempio, questo codice:

package temp.stackoverflow.q15849706; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.net.URL; 

import com.thoughtworks.xstream.XStream; 

public class ReadXmlLines { 
    public String read1(BufferedReader br) throws IOException { 
     try { 
      String s = ""; 
      String tempString; 
      int i = 0; 
      while ((tempString = br.readLine()) != null) { 
       s = s.concat(tempString); 
       // s=s+tempString; 
       i = i + 1; 
       if (i % 1000 == 0) { 
        System.out.println(Integer.toString(i)); 
       } 
      } 
      return s; 
     } finally { 
      br.close(); 
     } 
    } 

    public static void main(String[] args) throws IOException { 
     ReadXmlLines r = new ReadXmlLines(); 

     URL url = ReadXmlLines.class.getResource("xml.xml"); 
     String xmlStr = r.read1(new BufferedReader(new InputStreamReader(url 
       .openStream()))); 

     Object ob = null; 

     XStream xs = new XStream(); 
     xs.alias("root", Root.class); 

     // This is incorrectly read/parsed, as the line endings are not 
     // preserved. 
     System.out.println("----------1"); 
     System.out.println(xmlStr); 
     ob = xs.fromXML(xmlStr); 
     System.out.println(ob); 

     // This is correctly read/parsed, when passing in the URL directly 
     ob = xs.fromXML(url); 
     System.out.println("----------2"); 
     System.out.println(ob); 

     // This is correctly read/parsed, when passing in the InputStream 
     // directly 
     ob = xs.fromXML(url.openStream()); 
     System.out.println("----------3"); 
     System.out.println(ob); 
    } 

    public static class Root { 
     public String script; 

     public String toString() { 
      return script; 
     } 
    } 
} 

e questo file xml.xml nel classpath (nello stesso pacchetto della classe):

<root> 
    <script> 
<![CDATA[ 
// taken from http://www.w3schools.com/xml/xml_cdata.asp 
function matchwo(a,b) 
{ 
if (a < b && a < 0) then 
    { 
    return 1; 
    } 
else 
    { 
    return 0; 
    } 
} 
]]> 
    </script> 
</root> 

produce l'uscita seguente. Le prime due righe mostrano che le terminazioni di riga sono state rimosse, e quindi hanno invalidato il Javascript nella sezione CDATA (poiché il primo commento JS ora commenta l'intero JS, poiché le linee JS sono state unite).

----------1 
<root> <script><![CDATA[// taken from http://www.w3schools.com/xml/xml_cdata.aspfunction matchwo(a,b){if (a < b && a < 0) then { return 1; }else { return 0; }}]]> </script></root> 
// taken from http://www.w3schools.com/xml/xml_cdata.aspfunction matchwo(a,b){if (a < b && a < 0) then { return 1; }else { return 0; }}  
----------2 


// taken from http://www.w3schools.com/xml/xml_cdata.asp 
function matchwo(a,b) 
{ 
if (a < b && a < 0) then 
    { 
    return 1; 
    } 
else 
    { 
    return 0; 
    } 
} 
...