2013-04-06 3 views
10

У меня есть текстовый файл (XML, созданный с помощью XStream), длина которого составляет 63000 строк (3,5 МБ). Я пытаюсь прочитать его с помощью буферизованного считывателя:Java чтение длинного текстового файла очень медленно

   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(); 

Здесь вы можете увидеть мои попытки измерения скорости чтения. И это очень низко. Для считывания 1000 строк после 10000 строк требуется несколько секунд. Я явно делаю что-то неправильно, но не могу понять, что. Заранее спасибо за вашу помощь.

+0

Вы хотите проанализировать этот файл? Почему бы просто не загрузить его с помощью инструмента Xerces/SAX/другого синтаксического анализа? –

+10

Строки '+' и 'concat' очень неэффективны, если строки большие. Используйте 'StringBuilder' или передайте' InputStream'/'Reader' прямо в синтаксический анализатор xml. –

+0

Если вам действительно нужны строки, используйте что-то вроде этого - http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#readLines%28java .io.Reader% 29. –

ответ

4

@PaulGrime является правильным. Вы копируете строку каждый раз, когда цикл читает строку. Как только строка становится большой (скажем, 10 000 строк), она делает много работы, чтобы сделать это копирование.

Попробуйте это:

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

s = sb.toString(); 

Примечание: прочитать ответ Павла ниже, почему зачистки новой строки делает это плохой способ чтения в файле. Кроме того, как упоминалось в комментариях к вопросу, XStream предоставляет способ прочитать файл, и даже если бы он этого не сделал, IOUtils.toString (reader) был бы более безопасным способом чтения файла.

+0

Спасибо! Действительно ускоренная загрузка. – lozga

+1

-1 штраф за исполнение не копируется, Stringbuilder - это тот, который рекомендуется в документации, «PaulGrime is right» на самом деле не является ответом, который заслуживает того, чтобы его приняли ... и 10000? Зачем? – UmNyobe

+0

Я сказал, «скажем, 10 000», что означает «например, когда 10 000 строк большие». Я также объяснил, почему Павел прав, и привел пример кода. Кроме того, проясните, что вы подразумеваете под «не просто копированием». –

4

Некоторые немедленные улучшения, которые вы можете сделать:

  • Используйте StringBuilder вместо concat и +. Использование + и concat действительно может повлиять на производительность, особенно при использовании в цикле.
  • Уменьшите доступ к диску. Вы можете сделать это, используя large buffer:

    BufferedReader br = новый BufferedReader (новый FileReader ("someFile.txt"), SIZE);

1

Вы должны использовать StringBuilder в String конкатенация а является чрезвычайно медленно даже для небольших строк.

Кроме того, попробуйте использовать NIO, а не 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(); 
     } 
    } 
} 

Вы можете настроить размер буфера, если это все-таки слишком медленно - это в значительной степени зависит от того, что размер буфера работает лучше система. Для меня очень мало разница, если буфер 1K или 4K, но в других системах, которые я знаю, это изменение для увеличения скорости на порядок.

1

В дополнение к тому, что уже было сказано, в зависимости от использования вами XML ваш код потенциально неверен, поскольку он отбрасывает окончания строк. Например, этот код:

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

и этот xml.xml файл по классам (в том же пакете, что и класс):

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

производит следующий вывод. Первые две строки показывают, что окончание строк было удалено, и поэтому Javascript в разделе CDATA недействителен (поскольку первый комментарий JS теперь комментирует весь JS, поскольку линии JS были объединены).

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