我需要一位Java专家的建议,关于内存问题。 我有一个很大的文件(大约1.5GB),我需要将这个文件分成很多小文件(例如100个小文件)。
我知道如何做(使用BufferedReader
),但我想知道是否有关于内存方面的建议或如何更快地完成它的技巧。
我的文件包含文本,不是二进制文件,每行大约有20个字符。
我需要一位Java专家的建议,关于内存问题。 我有一个很大的文件(大约1.5GB),我需要将这个文件分成很多小文件(例如100个小文件)。
我知道如何做(使用BufferedReader
),但我想知道是否有关于内存方面的建议或如何更快地完成它的技巧。
我的文件包含文本,不是二进制文件,每行大约有20个字符。
BufferedReader
或不使用并不重要。它不会像某些人暗示的那样显着增加内存消耗。最多只会使性能下降几个百分点。使用NIO也是如此。它只会提高可伸缩性,而不会增加内存使用。仅当您有数百个线程运行在同一个文件上时才会变得有趣。String encoding = "UTF-8";
int maxlines = 100;
BufferedReader reader = null;
BufferedWriter writer = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream("/bigfile.txt"), encoding));
int count = 0;
for (String line; (line = reader.readLine()) != null;) {
if (count++ % maxlines == 0) {
close(writer);
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/smallfile" + (count / maxlines) + ".txt"), encoding));
}
writer.write(line);
writer.newLine();
}
} finally {
close(writer);
close(reader);
}
line.getBytes(encoding)
。否则会出现混乱。字节长度取决于所使用的字符编码。如果您实际上不担心文本行,则最好使用 InputStream
/OutputStream
并计算传输的字节数。顺便说一下,不清楚您是指文件存储在数据库中还是文件分割参数存储在数据库中。如果文件实际上也存储在数据库中,则可能会占用大量内存。确切的解决方案将取决于所使用的数据库。 - BalusC首先,如果您的文件包含二进制数据,则使用BufferedReader
将是一个很大的错误(因为您将把数据转换为字符串,这是不必要的,并且很容易破坏数据);相反,您应该使用BufferedInputStream
。如果它是文本数据并且您需要沿换行符拆分它,则使用BufferedReader
是可以的(假设文件包含合理长度的行)。
关于内存,如果您使用一个大小适当的缓冲区(我会至少使用1MB来确保硬盘进行大部分连续读写),就不应该有任何问题。
如果速度成为问题,您可以查看java.nio
软件包 - 那些据说比java.io
更快。
它必须使用Java完成吗?也就是说,它需要跨平台吗?如果不需要,我建议在*nix中使用'split'命令。如果你真的想要的话,你可以通过你的Java程序执行这个命令。虽然我没有测试过,但我想它比你能想出的任何Java IO实现都要快。
http://java.sun.com/javase/6/docs/technotes/guides/io/index.html
是的。 我也认为使用read()方法并带有参数,例如read(Char[], int init, int end)是读取如此大的文件的更好方式(例如:read(buffer,0,buffer.length))。
我也曾经遇到过使用BufferedReader而不是BufferedInputStreamReader读取二进制数据输入流时出现缺失值的问题。因此,在这种情况下,使用BufferedInputStreamReader会更好。
package all.is.well;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import junit.framework.TestCase;
/**
* @author Naresh Bhabat
*
Following implementation helps to deal with extra large files in java.
This program is tested for dealing with 2GB input file.
There are some points where extra logic can be added in future.
Pleasenote: if we want to deal with binary input file, then instead of reading line,we need to read bytes from read file object.
It uses random access file,which is almost like streaming API.
* ****************************************
Notes regarding executor framework and its readings.
Please note :ExecutorService executor = Executors.newFixedThreadPool(10);
* for 10 threads:Total time required for reading and writing the text in
* :seconds 349.317
*
* For 100:Total time required for reading the text and writing : seconds 464.042
*
* For 1000 : Total time required for reading and writing text :466.538
* For 10000 Total time required for reading and writing in seconds 479.701
*
*
*/
public class DealWithHugeRecordsinFile extends TestCase {
static final String FILEPATH = "C:\\springbatch\\bigfile1.txt.txt";
static final String FILEPATH_WRITE = "C:\\springbatch\\writinghere.txt";
static volatile RandomAccessFile fileToWrite;
static volatile RandomAccessFile file;
static volatile String fileContentsIter;
static volatile int position = 0;
public static void main(String[] args) throws IOException, InterruptedException {
long currentTimeMillis = System.currentTimeMillis();
try {
fileToWrite = new RandomAccessFile(FILEPATH_WRITE, "rw");//for random write,independent of thread obstacles
file = new RandomAccessFile(FILEPATH, "r");//for random read,independent of thread obstacles
seriouslyReadProcessAndWriteAsynch();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());
long currentTimeMillis2 = System.currentTimeMillis();
double time_seconds = (currentTimeMillis2 - currentTimeMillis) / 1000.0;
System.out.println("Total time required for reading the text in seconds " + time_seconds);
}
/**
* @throws IOException
* Something asynchronously serious
*/
public static void seriouslyReadProcessAndWriteAsynch() throws IOException {
ExecutorService executor = Executors.newFixedThreadPool(10);//pls see for explanation in comments section of the class
while (true) {
String readLine = file.readLine();
if (readLine == null) {
break;
}
Runnable genuineWorker = new Runnable() {
@Override
public void run() {
// do hard processing here in this thread,i have consumed
// some time and ignore some exception in write method.
writeToFile(FILEPATH_WRITE, readLine);
// System.out.println(" :" +
// Thread.currentThread().getName());
}
};
executor.execute(genuineWorker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
file.close();
fileToWrite.close();
}
/**
* @param filePath
* @param data
* @param position
*/
private static void writeToFile(String filePath, String data) {
try {
// fileToWrite.seek(position);
data = "\n" + data;
if (!data.contains("Randomization")) {
return;
}
System.out.println("Let us do something time consuming to make this thread busy"+(position++) + " :" + data);
System.out.println("Lets consume through this loop");
int i=1000;
while(i>0){
i--;
}
fileToWrite.write(data.getBytes());
throw new Exception();
} catch (Exception exception) {
System.out.println("exception was thrown but still we are able to proceeed further"
+ " \n This can be used for marking failure of the records");
//exception.printStackTrace();
}
}
}
除非您意外地读取了整个输入文件而不是逐行读取,否则您的主要限制将是磁盘速度。您可以尝试从包含100行的文件开始,并将其写入100个不同的文件中,每个文件一行,并使触发机制根据写入当前文件的行数工作。该程序将很容易扩展到您的情况。