나는 거대한 데이터 파일을 사용하며 때로는이 파일의 줄 수만 알아야합니다. 일반적으로 파일을 열고 파일 끝에 도달 할 때까지 한 줄씩 읽습니다.
더 똑똑한 방법이 있는지 궁금합니다.
답변
이것은 지금까지 찾은 가장 빠른 버전으로 readLine보다 약 6 배 빠릅니다. 150MB 로그 파일에서는 readLines ()를 사용할 때 2.40 초와 비교하여 0.35 초가 걸립니다. linux의 wc -l 명령은 0.15 초가 걸립니다.
public static int countLinesOld(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean empty = true;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
}
return (count == 0 && !empty) ? 1 : count;
} finally {
is.close();
}
}
편집, 9 1/2 년 후 : 나는 실제로 자바 경험이 없지만 어쨌든 LineNumberReader
아무도 그것을하지 않았다는 이유로 귀찮게하기 때문에 아래 솔루션 에 대해이 코드를 벤치 마크하려고 했습니다. 특히 큰 파일의 경우 내 솔루션이 더 빠릅니다. 옵티마이 저가 적절한 작업을 수행 할 때까지 몇 번의 실행이 필요한 것 같습니다. 나는 코드로 조금 연주했으며 지속적으로 가장 빠른 새 버전을 만들었습니다.
public static int countLinesNew(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int readChars = is.read(c);
if (readChars == -1) {
// bail out if nothing to read
return 0;
}
// make it easy for the optimizer to tune this loop
int count = 0;
while (readChars == 1024) {
for (int i=0; i<1024;) {
if (c[i++] == '\n') {
++count;
}
}
readChars = is.read(c);
}
// count remaining characters
while (readChars != -1) {
System.out.println(readChars);
for (int i=0; i<readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
readChars = is.read(c);
}
return count == 0 ? 1 : count;
} finally {
is.close();
}
}
벤치 마크는 1.3GB 텍스트 파일, y 축 (초)입니다. 나는 같은 파일로 100 회 실행을 수행하고을 사용하여 각 실행을 측정했습니다 System.nanoTime()
. 당신은 countLinesOld
몇 가지 특이 치가 countLinesNew
있고 아무것도 없다는 것을 알 수 있으며 조금 더 빠르지 만 그 차이는 통계적으로 중요합니다. LineNumberReader
분명히 느리다.
답변
문제에 대한 다른 솔루션을 구현했는데 행을 계산하는 것이 더 효율적이라는 것을 알았습니다.
try
(
FileReader input = new FileReader("input.txt");
LineNumberReader count = new LineNumberReader(input);
)
{
while (count.skip(Long.MAX_VALUE) > 0)
{
// Loop just in case the file is > Long.MAX_VALUE or skip() decides to not read the entire file
}
result = count.getLineNumber() + 1; // +1 because line index starts at 0
}
답변
수락 된 답변에는 줄 바꿈으로 끝나지 않는 여러 줄 파일에 대해 하나의 오류가 있습니다. 줄 바꿈없이 끝나는 한 줄 파일은 1을 반환하지만 줄 바꿈없이 끝나는 두 줄 파일도 1을 반환합니다. 다음은이를 해결하는 수용 솔루션의 구현입니다. endsWithoutNewLine 검사는 최종 읽기 이외의 모든 것에 대해 낭비이지만 전체 기능에 비해 시간이 현명하지 않아야합니다.
public int count(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean endsWithoutNewLine = false;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n')
++count;
}
endsWithoutNewLine = (c[readChars - 1] != '\n');
}
if(endsWithoutNewLine) {
++count;
}
return count;
} finally {
is.close();
}
}
답변
와 자바 -8스트림을 사용할 수 있습니다.
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
답변
위의 count () 메소드의 대답은 파일 끝에 줄 바꿈이 없으면 파일의 마지막 줄을 세지 못했습니다.
이 방법은 나에게 더 효과적입니다.
public int countLines(String filename) throws IOException {
LineNumberReader reader = new LineNumberReader(new FileReader(filename));
int cnt = 0;
String lineRead = "";
while ((lineRead = reader.readLine()) != null) {}
cnt = reader.getLineNumber();
reader.close();
return cnt;
}
답변
나는 이것이 오래된 질문이라는 것을 알고 있지만 수용 된 해결책은 내가 해야하는 것과 일치하지 않았다. 따라서 줄 바꿈이 아닌 다양한 줄 종결자를 수락하고 지정된 문자 인코딩 (ISO-8859- n 대신)을 사용하도록 수정했습니다 . 한 가지 방법으로 모두 (적절한 리 팩터) :
public static long getLinesCount(String fileName, String encodingName) throws IOException {
long linesCount = 0;
File file = new File(fileName);
FileInputStream fileIn = new FileInputStream(file);
try {
Charset encoding = Charset.forName(encodingName);
Reader fileReader = new InputStreamReader(fileIn, encoding);
int bufferSize = 4096;
Reader reader = new BufferedReader(fileReader, bufferSize);
char[] buffer = new char[bufferSize];
int prevChar = -1;
int readCount = reader.read(buffer);
while (readCount != -1) {
for (int i = 0; i < readCount; i++) {
int nextChar = buffer[i];
switch (nextChar) {
case '\r': {
// The current line is terminated by a carriage return or by a carriage return immediately followed by a line feed.
linesCount++;
break;
}
case '\n': {
if (prevChar == '\r') {
// The current line is terminated by a carriage return immediately followed by a line feed.
// The line has already been counted.
} else {
// The current line is terminated by a line feed.
linesCount++;
}
break;
}
}
prevChar = nextChar;
}
readCount = reader.read(buffer);
}
if (prevCh != -1) {
switch (prevCh) {
case '\r':
case '\n': {
// The last line is terminated by a line terminator.
// The last line has already been counted.
break;
}
default: {
// The last line is terminated by end-of-file.
linesCount++;
}
}
}
} finally {
fileIn.close();
}
return linesCount;
}
이 솔루션은 수용 된 솔루션과 속도가 비슷하며 테스트에서 약 4 % 느립니다 (Java의 타이밍 테스트는 신뢰할 수 없음).
답변
위의 라인 계산 방법을 테스트했으며 다음은 시스템에서 테스트 한 다른 방법에 대한 관찰 결과입니다.
파일 크기 : 1.6 Gb 방법 :
- 스캐너 사용 : 약 35 초
- BufferedReader 사용 : 약 5 초
- Java 8 사용 : 약 5 초
- LineNumberReader 사용 : 약 5 초
또한 Java8 접근법은 매우 편리합니다.
Files.lines(Paths.get(filePath), Charset.defaultCharset()).count()
[Return type : long]