简短回答,做这个:
public static String readFile( String filePath ) throws IOException
{
Reader reader = new FileReader( filePath );
StringBuilder sb = new StringBuilder();
char buffer[] = new char[16384];
int len;
while( ( len = reader.read( buffer ) ) > 0 ){
sb.append( buffer, 0, len );
}
reader.close();
return sb.toString();
}
这是非常简单直接的方法,速度很快,并且适用于超大文本文件(100+ MB)。
长篇回答:
虽然有时候并不重要,但是这种方法相当快速且易读。实际上,它比@Raceimation的答案复杂度更低——O(n)而不是O(n^2)。
我测试了六种方法(从慢到快):
- concat:逐行读取并使用str +=进行连接 *(即使对于较小的文件,这也令人担忧地缓慢(对于一个3MB文件需要约70秒))*
- 猜测长度的StringBuilder:使用文件大小初始化StringBuilder。我猜测它很慢是因为它确实试图找到如此巨大的线性内存块。
- 带有行缓冲区的StringBuilder:StringBuilder,按行读取文件
- 带有char[]缓冲区的StringBuffer:使用StringBuffer连接,以16k块方式读取文件
- 带有char[]缓冲区的StringBuilder:使用StringBuilder连接,在16k块中读取文件
- 预分配byte[filesize]缓冲区:分配一个大小为文件大小的byte[]缓冲区,并让Java API决定如何缓冲单个块。
结论:
完全预分配缓冲区是处理超大文件时最快的方法,但该方法不太灵活,因为必须提前知道总文件大小。这就是为什么我建议使用带有char[]缓冲区的strBuilder,它仍然简单易用,如果需要可以轻松更改以接受任何输入流而不仅仅是文件。但是它对于所有合理情况来说速度足够快。
测试结果+代码
import java.io.*;
public class Test
{
static final int N = 5;
public final static void main( String args[] ) throws IOException{
test( "1k.txt", true );
test( "10k.txt", true );
test( "100k.txt", false );
test( "2142k.txt", false );
test( "pruned-names.csv", false );
test( "/Users/hansi/Downloads/xcode46graphicstools6938140a.dmg", false );
}
public static void test( String file, boolean includeConcat ) throws IOException{
System.out.println( "Reading " + file + " (~" + (new File(file).length()/1024) + "Kbytes)" );
strbuilderwithchars( file );
strbuilderwithchars( file );
strbuilderwithchars( file );
tick( "Warm up... " );
if( includeConcat ){
for( int i = 0; i < N; i++ )
concat( file );
tick( "> Concat with += " );
}
else{
tick( "> Concat with += **skipped** " );
}
for( int i = 0; i < N; i++ )
strbuilderguess( file );
tick( "> StringBuilder init with length " );
for( int i = 0; i < N; i++ )
strbuilder( file );
tick( "> StringBuilder with line buffer " );
for( int i = 0; i < N; i++ )
strbuilderwithchars( file );
tick( "> StringBuilder with char[] buffer" );
for( int i = 0; i < N; i++ )
strbufferwithchars( file );
tick( "> StringBuffer with char[] buffer " );
for( int i = 0; i < N; i++ )
singleBuffer( file );
tick( "> Allocate byte[filesize] " );
System.out.println();
}
public static long now = System.currentTimeMillis();
public static void tick( String message ){
long t = System.currentTimeMillis();
System.out.println( message + ": " + ( t - now )/N + " ms" );
now = t;
}
public static String strbuilderwithchars( String filePath ) throws IOException
{
Reader reader = new FileReader( filePath );
StringBuilder sb = new StringBuilder();
char buffer[] = new char[16384];
int len;
while( ( len = reader.read( buffer ) ) > 0 ){
sb.append( buffer, 0, len );
}
reader.close();
return sb.toString();
}
public static String strbufferwithchars( String filePath ) throws IOException
{
Reader reader = new FileReader( filePath );
StringBuffer sb = new StringBuffer();
char buffer[] = new char[16384];
int len;
while( ( len = reader.read( buffer ) ) > 0 ){
sb.append( buffer, 0, len );
}
reader.close();
return sb.toString();
}
public static String strbuilderguess(String filePath) throws IOException
{
File file = new File( filePath );
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
StringBuilder sb = new StringBuilder( (int)file.length() );
while( ( line = reader.readLine() ) != null)
{
sb.append( line );
}
reader.close();
return sb.toString();
}
public static String strbuilder(String filePath) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String line;
StringBuilder sb = new StringBuilder();
while( ( line = reader.readLine() ) != null)
{
sb.append( line );
}
reader.close();
return sb.toString();
}
public static String concat(String filePath) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String line, results = "";
int i = 0;
while( ( line = reader.readLine() ) != null)
{
results += line;
i++;
}
reader.close();
return results;
}
public static String singleBuffer(String filePath ) throws IOException{
FileInputStream in = new FileInputStream( filePath );
byte buffer[] = new byte[(int) new File( filePath).length()];
int len = in.read( buffer );
return new String( buffer, 0, len );
}
}
顺便提一下,您可能已经注意到StringBuffer比StringBuilder稍微快一些。这有点荒谬,因为这两个类是相同的,除了StringBuilder不是同步的。如果有人能够(或者)不能重现这个问题......我最好奇了 :)
javap -c YourClass
。我不太擅长阅读这些东西,但我认为大致上是这样的:String results = ""; while( ( line = readline() ) != null ) results = new StringBuilder( results ).append ( line ).toString();
当然,这是一种浪费资源的方式,因为在每个循环中都会分配一个StringBuilder并转换为字符串,但很多时候这并不重要。现在添加一个更有效的替代方案。 - kritzikratzi