Java读取长文本文件很慢
Posted
技术标签:
【中文标题】Java读取长文本文件很慢【英文标题】:Java reading long text file is very slow 【发布时间】: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,而不是自己读完再传字符串。
【参考方案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.***.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;
...
【讨论】:
以上是关于Java读取长文本文件很慢的主要内容,如果未能解决你的问题,请参考以下文章
是否有可能知道从文件中读取的长文本将在 C 中使用多少个字符?