【问题标题】:Java reading long text file is very slowJava读取长文本文件很慢
【发布时间】:2013-03-28 19:11:48
【问题描述】:

我有一个 63000 行 (3.5 MB) 长的文本文件(使用 XStream 创建的 XML)。我正在尝试使用缓冲阅读器阅读它:

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

在这里你可以看到我测量阅读速度的尝试。而且非常低。在 10000 行之后读取 1000 行需要几秒钟。我显然做错了什么,但不明白是什么。提前感谢您的帮助。

【问题讨论】:

  • 你打算解析这个文件吗?为什么不直接用 Xerces/SAX/其他解析工具来加载呢?
  • String +concat 如果字符串很大,则效率非常低。使用StringBuilder 或将InputStream/Reader 直接传递给xml 解析器。
  • 或者如果你真的需要线条,使用类似这样的东西 - commons.apache.org/proper/commons-io/javadocs/api-2.4/org/…
  • 是的,我正在尝试解析这个文件并再次将其输入 Xstream 以读取保存的类。行并不重要。
  • 如果在XStream中需要的话,不如直接把reader传给XStream,而不是自己读完再传字符串。

标签: java file-io bufferedreader file-read


【解决方案1】:

@PaulGrime 是对的。每次循环读取一行时,您都在复制字符串。一旦字符串变大(比如 10,000 行大),它就会做很多工作来进行复制。

试试这个:

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

s = sb.toString();

注意:请阅读下面 Paul 的回答,了解为什么剥离换行符会使这是一种读取文件的不好方法。此外,正如问题 cmets 中所述,XStream 提供了一种读取文件的方法,即使它没有,IOUtils.toString(reader) 也是一种更安全的读取文件的方法。

【讨论】:

  • 谢谢!确实加快了加载速度。
  • -1 性能损失只是不复制,Stringbuilder 是文档中建议的那个,PaulGrime is right 并不是真正值得接受的答案......和10000?为什么?
  • 我说,“say 10,000”的意思是,“例如,当 10,000 行大时”。我还解释了为什么 Paul 是对的,并给出了一个代码示例。另外,请澄清“不仅仅是复制”的意思。
  • 我也从 StringBuffer 更改为 StringBuilder - 它们是等效的类,但 StringBuilder 不是线程安全的(在这种情况下很好)。
【解决方案2】:

您可以立即进行一些改进:

  • 使用StringBuilder 代替concat+。使用+concat 确实会影响性能,尤其是在循环中使用时。
  • 减少对磁盘的访问。您可以使用large buffer

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

【讨论】:

    【解决方案3】:

    您应该使用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 几乎没有区别,但在其他系统上,我知道这种变化可以将速度提高一个数量级。

    【讨论】:

      【解决方案4】:

      除了已经说过的之外,根据您对 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>
      

      产生以下输出。前两行显示行尾已被删除,因此使 CDATA 部分中的 Javascript 无效(因为第一个 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;
        }
      }
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-17
        • 2022-07-06
        • 2011-07-18
        • 1970-01-01
        • 1970-01-01
        • 2019-06-16
        • 2016-08-08
        • 1970-01-01
        相关资源
        最近更新 更多