Java로 대용량 파일 읽기
Java와 메모리 문제를 잘 아는 사람의 조언이 필요합니다. 큰 파일 (예 : 1.5GB)이 있고이 파일을 많은 (예 : 작은 파일 100 개) 작은 파일로 잘라야합니다.
나는 일반적으로 그것을하는 방법을 알고 BufferedReader
있지만 (를 사용하여 ) 기억에 관한 조언이 있거나 그것을 더 빨리하는 방법에 대한 조언이 있는지 알고 싶습니다.
내 파일에는 텍스트가 포함되어 있으며 바이너리가 아니며 한 줄에 약 20 문자가 있습니다.
첫째, 파일에 이진 데이터가 포함 된 경우 사용 BufferedReader
하는 것은 큰 실수입니다 (데이터를 문자열로 변환 할 수 있으므로 불필요하고 데이터가 쉽게 손상 될 수 있기 때문입니다). BufferedInputStream
대신 a 를 사용해야합니다 . 텍스트 데이터이고 BufferedReader
줄 바꿈을 따라 분할해야하는 경우 사용하는 것이 좋습니다 (파일에 적절한 길이의 줄이 포함되어 있다고 가정).
메모리와 관련하여 적절한 크기의 버퍼를 사용하면 문제가 없을 것입니다 (HD가 대부분 순차적 읽기 및 쓰기를 수행하는지 확인하기 위해 최소 1MB를 사용합니다).
속도가 문제인 것으로 판명되면 java.nio
패키지를 살펴볼 수 있습니다. 패키지는 java.io
.
메모리를 절약하려면 데이터를 메모리에 불필요하게 저장 / 복제하지 마십시오 (즉, 루프 외부의 변수에 할당하지 마십시오). 입력이 들어오는 즉시 출력을 처리하십시오 .
사용 여부는 정말 중요하지 BufferedReader
않습니다. 일부는 암시 적으로 암시하는 것처럼 훨씬 더 많은 메모리를 소비하지 않습니다. 최고 수준에서는 성능에서 몇 %에 불과합니다. NIO 사용에도 동일하게 적용됩니다. 메모리 사용이 아닌 확장 성을 향상시킬뿐입니다. 동일한 파일에서 수백 개의 스레드가 실행 중일 때만 흥미로울 것입니다.
파일을 반복하고, 읽을 때마다 즉시 다른 파일에 모든 줄을 쓰고, 줄을 세고, 100에 도달하면 다음 파일로 전환하는 등의 작업을 수행하면됩니다.
킥오프 예 :
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);
}
FileChannel을 통해 메모리 매핑 된 파일 사용을 고려할 수 있습니다 .
일반적으로 대용량 파일의 경우 훨씬 빠릅니다. 속도를 늦출 수 있는 성능 절충점이 있으므로 YMMV입니다.
관련 답변 : Java NIO FileChannel 대 FileOutputstream 성능 / 유용성
이것은 아주 좋은 기사입니다 : http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/
요약하면 뛰어난 성능을 위해 다음을 수행해야합니다.
- 디스크에 액세스하지 마십시오.
- 기본 운영 체제에 액세스하지 마십시오.
- 메서드 호출을 피하십시오.
- 바이트와 문자를 개별적으로 처리하지 마십시오.
예를 들어 디스크에 대한 액세스를 줄이기 위해 큰 버퍼를 사용할 수 있습니다. 이 기사에서는 다양한 접근 방식을 설명합니다.
Java로해야합니까? 즉, 플랫폼 독립적이어야합니까? 그렇지 않은 경우 * nix에서 ' split '명령을 사용하는 것이 좋습니다 . 정말로 원한다면 자바 프로그램을 통해이 명령을 실행할 수 있습니다. 테스트하지는 않았지만 어떤 Java IO 구현보다 빠르게 수행 할 수 있다고 생각합니다.
기존 입출력 스트림보다 빠른 java.nio를 사용할 수 있습니다.
http://java.sun.com/javase/6/docs/technotes/guides/io/index.html
예. 또한 read ()를 read (Char [], int init, int end)와 같은 인수와 함께 사용하는 것이 그러한 큰 파일을 읽는 더 좋은 방법이라고 생각합니다 (예 : read (buffer, 0, buffer.length)).
또한 이진 데이터 입력 스트림에 BufferedInputStreamReader 대신 BufferedReader를 사용하는 경우 값이 누락되는 문제도 경험했습니다. 따라서 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();
}
}
}
인수없이 읽기를 사용하지 마십시오. 매우 느립니다. 더 잘 읽고 버퍼링하고 파일로 빠르게 이동하십시오.
이진 읽기를 지원하므로 bufferedInputStream을 사용하십시오.
그리고 그게 전부입니다.
한 줄씩 읽는 대신 실수로 전체 입력 파일을 읽지 않는 한 기본 제한은 디스크 속도입니다. 100 줄이 포함 된 파일로 시작하여 각 줄에 한 줄씩 100 개의 다른 파일에 기록하고 트리거링 메커니즘이 현재 파일에 기록 된 줄 수에 대해 작동하도록 할 수 있습니다. 이 프로그램은 귀하의 상황에 맞게 쉽게 확장 할 수 있습니다.
참고URL : https://stackoverflow.com/questions/2356137/read-large-files-in-java
'IT TIP' 카테고리의 다른 글
이 iPhone 6은 iOS 10.1 (14B55c)을 실행 중이며이 버전의 Xcode에서 지원하지 않을 수 있습니다. (0) | 2020.12.08 |
---|---|
SQL Server .bak 파일을 MySQL로 가져 오는 방법은 무엇입니까? (0) | 2020.12.08 |
파이썬의 선은 파이썬의지도 원리를 20 개의 격언으로 나누지 만 19 개만 나열합니다. 20 번째는 무엇입니까? (0) | 2020.12.08 |
여러 시스템 속성 설정 Java 명령 줄 (0) | 2020.12.08 |
Jinja2 : 루프 내부의 변수 값 변경 (0) | 2020.12.08 |