10

我有一个 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 行需要几秒钟。我显然做错了什么,但不明白是什么。在此先感谢您的帮助。

4

4 回答 4

4

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

试试这个:

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

s = sb.toString();

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

于 2013-04-06T10:33:52.400 回答
4

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

  • 使用StringBuilder代替concatand +。使用+andconcat确实会影响性能,特别是在循环中使用时。
  • 减少对磁盘的访问。您可以通过使用大缓冲区来做到这一点:

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

于 2013-04-06T10:34:04.183 回答
3

即使是小字符串,您也应该使用StringBuilderasString连接速度非常慢。

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

于 2013-04-06T10:40:30.337 回答
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>

产生以下输出。前两行显示行尾已被删除,因此使 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;
  }
}
...
于 2013-04-06T11:25:30.673 回答