텍스트 파일의 마지막 줄을 빨리 읽으시겠습니까?
Java의 [매우 매우 큰] 파일에서 텍스트의 마지막 줄을 읽는 가장 빠르고 효율적인 방법은 무엇입니까?
C #에 대한 비슷한 질문에 대한 내 대답을 살펴보십시오 . Java에서는 인코딩 지원이 약간 다르지만 코드는 매우 유사합니다.
기본적으로 일반적으로하는 것은 매우 쉬운 일이 아닙니다. MSalter가 지적 하듯이, UTF-8은 쉽게 발견 할 수 있도록 않습니다 \r
또는 \n
그 문자의 UTF-8 표현은 ASCII처럼 동일로, 그 바이트 멀티 바이트 문자에서 발생하지 않습니다.
따라서 기본적으로 2K의 버퍼를 취하고 점진적으로 역방향으로 읽습니다 (이전에 2K로 건너 뛰고 다음 2K를 읽음). 그런 다음 스트림의 정확한 위치로 건너 뛰고 InputStreamReader
맨 위에를 만들고 그 위에를 만듭니다 BufferedReader
. 그런 다음 BufferedReader.readLine()
.
다음은 전체 파일을로드하거나 단계별로 실행하지 않고 파일의 비어 있지 않은 마지막 줄을 반환하는 함수와 전체 파일을 단계별로 실행하지 않고 파일의 마지막 N 줄을 반환하는 두 가지 함수입니다.
tail이하는 일은 파일의 마지막 문자로 곧바로 확대 한 다음 문자 단위로 뒤로 이동하여 줄 바꿈을 찾을 때까지 보이는 내용을 기록하는 것입니다. 줄 바꿈을 찾으면 루프에서 나갑니다. 기록 된 내용을 되돌리고 문자열에 던져 반환합니다. 0xA는 새 줄이고 0xD는 캐리지 리턴입니다.
줄 끝이 \r\n
또는 crlf
다른 "이중 줄 바꿈 스타일 줄 바꿈"인 경우 모든 줄에 대해 2 줄을 계산하므로 마지막 n 줄을 얻기 위해 n * 2 줄을 지정해야합니다.
public String tail( File file ) {
RandomAccessFile fileHandler = null;
try {
fileHandler = new RandomAccessFile( file, "r" );
long fileLength = fileHandler.length() - 1;
StringBuilder sb = new StringBuilder();
for(long filePointer = fileLength; filePointer != -1; filePointer--){
fileHandler.seek( filePointer );
int readByte = fileHandler.readByte();
if( readByte == 0xA ) {
if( filePointer == fileLength ) {
continue;
}
break;
} else if( readByte == 0xD ) {
if( filePointer == fileLength - 1 ) {
continue;
}
break;
}
sb.append( ( char ) readByte );
}
String lastLine = sb.reverse().toString();
return lastLine;
} catch( java.io.FileNotFoundException e ) {
e.printStackTrace();
return null;
} catch( java.io.IOException e ) {
e.printStackTrace();
return null;
} finally {
if (fileHandler != null )
try {
fileHandler.close();
} catch (IOException e) {
/* ignore */
}
}
}
그러나 아마도 마지막 줄이 필요하지 않고 마지막 N 줄이 필요하므로 대신 다음을 사용하십시오.
public String tail2( File file, int lines) {
java.io.RandomAccessFile fileHandler = null;
try {
fileHandler =
new java.io.RandomAccessFile( file, "r" );
long fileLength = fileHandler.length() - 1;
StringBuilder sb = new StringBuilder();
int line = 0;
for(long filePointer = fileLength; filePointer != -1; filePointer--){
fileHandler.seek( filePointer );
int readByte = fileHandler.readByte();
if( readByte == 0xA ) {
if (filePointer < fileLength) {
line = line + 1;
}
} else if( readByte == 0xD ) {
if (filePointer < fileLength-1) {
line = line + 1;
}
}
if (line >= lines) {
break;
}
sb.append( ( char ) readByte );
}
String lastLine = sb.reverse().toString();
return lastLine;
} catch( java.io.FileNotFoundException e ) {
e.printStackTrace();
return null;
} catch( java.io.IOException e ) {
e.printStackTrace();
return null;
}
finally {
if (fileHandler != null )
try {
fileHandler.close();
} catch (IOException e) {
}
}
}
다음과 같이 위의 메소드를 호출하십시오.
File file = new File("D:\\stuff\\huge.log");
System.out.println(tail(file));
System.out.println(tail2(file, 10));
경고 유니 코드의 서부에서이 코드로 인해이 함수의 출력이 잘못 나올 수 있습니다. 예를 들어 "Mary 's"대신 "Mary? s"입니다. 와 캐릭터 모자, 악센트, 한자 등 악센트가 문자 다음 수식으로 추가되기 때문에 출력이 잘못 될 수 있습니다. 복합 문자를 반전하면 반전시 캐릭터의 정체성 특성이 변경됩니다. 이를 사용하려는 모든 언어에 대해 전체 배터리 테스트를 수행해야합니다.
이 유니 코드 반전 문제에 대한 자세한 내용은 http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx를 읽으십시오 .
Apache Commons에는 RandomAccessFile을 사용하는 구현이 있습니다.
ReversedLinesFileReader 라고 합니다.
FileReader 또는 FileInputStream을 사용하면 작동하지 않습니다. FileChannel 또는 RandomAccessFile 을 사용 하여 파일을 끝에서 뒤로 반복해야합니다. Jon이 말했듯이 인코딩은 문제가 될 것입니다.
내가 아는 한 텍스트 파일의 마지막 줄을 읽는 가장 빠른 방법은 "org.apache.commons.io"에있는 FileUtils Apache 클래스를 사용하는 것입니다. 2 백만 줄의 파일이 있는데이 클래스를 사용하여 마지막 줄을 찾는 데 1 초도 걸리지 않았습니다. 내 코드는 다음과 같습니다.
LineIterator lineIterator = FileUtils.lineIterator(newFile(filePath),"UTF-8");
String lastLine="";
while (lineIterator.hasNext()){
lastLine= lineIterator.nextLine();
}
아래 코드를 쉽게 변경하여 마지막 줄을 인쇄 할 수 있습니다.
마지막 5 줄을 인쇄하기위한 MemoryMappedFile :
private static void printByMemoryMappedFile(File file) throws FileNotFoundException, IOException{
FileInputStream fileInputStream=new FileInputStream(file);
FileChannel channel=fileInputStream.getChannel();
ByteBuffer buffer=channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
buffer.position((int)channel.size());
int count=0;
StringBuilder builder=new StringBuilder();
for(long i=channel.size()-1;i>=0;i--){
char c=(char)buffer.get((int)i);
builder.append(c);
if(c=='\n'){
if(count==5)break;
count++;
builder.reverse();
System.out.println(builder.toString());
builder=null;
builder=new StringBuilder();
}
}
channel.close();
}
마지막 5 줄을 인쇄하기위한 RandomAccessFile :
private static void printByRandomAcessFile(File file) throws FileNotFoundException, IOException{
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
int lines = 0;
StringBuilder builder = new StringBuilder();
long length = file.length();
length--;
randomAccessFile.seek(length);
for(long seek = length; seek >= 0; --seek){
randomAccessFile.seek(seek);
char c = (char)randomAccessFile.read();
builder.append(c);
if(c == '\n'){
builder = builder.reverse();
System.out.println(builder.toString());
lines++;
builder = null;
builder = new StringBuilder();
if (lines == 5){
break;
}
}
}
}
try(BufferedReader reader = new BufferedReader(new FileReader(reqFile))) {
String line = null;
System.out.println("======================================");
line = reader.readLine(); //Read Line ONE
line = reader.readLine(); //Read Line TWO
System.out.println("first line : " + line);
//Length of one line if lines are of even length
int len = line.length();
//skip to the end - 3 lines
reader.skip((reqFile.length() - (len*3)));
//Searched to the last line for the date I was looking for.
while((line = reader.readLine()) != null){
System.out.println("FROM LINE : " + line);
String date = line.substring(0,line.indexOf(","));
System.out.println("DATE : " + date); //BAM!!!!!!!!!!!!!!
}
System.out.println(reqFile.getName() + " Read(" + reqFile.length()/(1000) + "KB)");
System.out.println("======================================");
} catch (IOException x) {
x.printStackTrace();
}
Path path = Paths.get(pathString);
List<String> allLines = Files.readAllLines(path);
return allLines.get(allLines.size()-1);
C # 에서는 스트림 위치를 설정할 수 있어야합니다.
From: http://bytes.com/groups/net-c/269090-streamreader-read-last-line-text-file
using(FileStream fs = File.OpenRead("c:\\file.dat"))
{
using(StreamReader sr = new StreamReader(fs))
{
sr.BaseStream.Position = fs.Length - 4;
if(sr.ReadToEnd() == "DONE")
// match
}
}
참고URL : https://stackoverflow.com/questions/686231/quickly-read-the-last-line-of-a-text-file
'Development Tip' 카테고리의 다른 글
파일 또는 어셈블리 'Microsoft.Build.Framework'(VS 2017)를로드 할 수 없습니다. (0) | 2020.12.10 |
---|---|
ArgumentNullException 던지기 (0) | 2020.12.10 |
직사각형에서 정사각형을 도출하는 것은 Liskov의 대체 원리를 위반하는 것입니까? (0) | 2020.12.10 |
HeaderTemplate에서 중첩 된 repeater의 상위 데이터에 액세스 (0) | 2020.12.10 |
Swift에서 UIView에 xib 할당 (0) | 2020.12.09 |