답변:
이것은 지금까지 찾은 가장 빠른 버전으로 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
}
LineNumberReader
의 lineNumber
필드는 정수입니다. Integer.MAX_VALUE보다 긴 파일 만 래핑하지 않습니까? 왜 여기서 길게 건너 뛰는가?
wc -l
파일의 개행 문자 수를 계산합니다. 이것은 모든 줄이 파일의 마지막 줄을 포함하여 줄 바꿈으로 끝나기 때문에 작동합니다. 모든 줄에는 빈 줄을 포함하여 줄 바꿈 문자가 있으므로 줄 바꿈 문자 수 == 파일의 줄 수입니다. 이제 lineNumber
in 변수 FileNumberReader
는 줄 바꿈 문자 수를 나타냅니다. 개행이 발견되기 전에 0에서 시작하며 모든 개행 문자가 표시 될 때마다 증가합니다. 따라서 줄 번호에 하나를 추가하지 마십시오.
wc -l
이런 종류의 파일을보고 하는 방법 입니다. stackoverflow.com/questions/729692/…
wc -l
을 반환하고 1을 반환합니다. 나는 모든 방법에 결함이 있다고 결론 내렸다.
수락 된 답변에는 줄 바꿈으로 끝나지 않는 여러 줄 파일에 대해 하나의 오류가 있습니다. 줄 바꿈없이 끝나는 한 줄 파일은 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;
}
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의 타이밍 테스트는 신뢰할 수 없음).
/**
* Count file rows.
*
* @param file file
* @return file row count
* @throws IOException
*/
public static long getLineCount(File file) throws IOException {
try (Stream<String> lines = Files.lines(file.toPath())) {
return lines.count();
}
}
JDK8_u31에서 테스트되었습니다. 그러나 실제로이 방법에 비해 성능이 느립니다.
/**
* Count file rows.
*
* @param file file
* @return file row count
* @throws IOException
*/
public static long getLineCount(File file) throws IOException {
try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(file), 1024)) {
byte[] c = new byte[1024];
boolean empty = true,
lastEmpty = false;
long count = 0;
int read;
while ((read = is.read(c)) != -1) {
for (int i = 0; i < read; i++) {
if (c[i] == '\n') {
count++;
lastEmpty = true;
} else if (lastEmpty) {
lastEmpty = false;
}
}
empty = false;
}
if (!empty) {
if (count == 0) {
count = 1;
} else if (!lastEmpty) {
count++;
}
}
return count;
}
}
테스트되고 매우 빠릅니다.
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
그리고 라인의 수는 잘못된 너무 짝수
BufferedInputStream
어쨌든 자신의 버퍼를 읽을 때를 사용해서는 안됩니다 . 또한, 분석법에 약간의 성능 이점이 있더라도 \r
더 이상 단독 라인 터미네이터 (이전 MacOS)를 지원하지 않으며 모든 인코딩을 지원하지 않기 때문에 유연성이 떨어 집니다.
스캐너를 사용하는 간단한 방법
static void lineCounter (String path) throws IOException {
int lineCount = 0, commentsCount = 0;
Scanner input = new Scanner(new File(path));
while (input.hasNextLine()) {
String data = input.nextLine();
if (data.startsWith("//")) commentsCount++;
lineCount++;
}
System.out.println("Line Count: " + lineCount + "\t Comments Count: " + commentsCount);
}
줄 wc -l
바꿈을 계산 하는 : s 방법은 훌륭하지만 마지막 줄이 줄 바꿈으로 끝나지 않는 파일에 대해서는 직관적이지 않은 결과를 반환합니다.
LineNumberReader를 기반으로하는 @ er.vikas 솔루션이지만 줄 수에 1을 추가하면 마지막 줄이 줄 바꿈으로 끝나는 파일에 직관적이지 않은 결과가 반환됩니다.
따라서 다음과 같이 처리하는 알고리즘을 만들었습니다.
@Test
public void empty() throws IOException {
assertEquals(0, count(""));
}
@Test
public void singleNewline() throws IOException {
assertEquals(1, count("\n"));
}
@Test
public void dataWithoutNewline() throws IOException {
assertEquals(1, count("one"));
}
@Test
public void oneCompleteLine() throws IOException {
assertEquals(1, count("one\n"));
}
@Test
public void twoCompleteLines() throws IOException {
assertEquals(2, count("one\ntwo\n"));
}
@Test
public void twoLinesWithoutNewlineAtEnd() throws IOException {
assertEquals(2, count("one\ntwo"));
}
@Test
public void aFewLines() throws IOException {
assertEquals(5, count("one\ntwo\nthree\nfour\nfive\n"));
}
그리고 다음과 같이 보입니다 :
static long countLines(InputStream is) throws IOException {
try(LineNumberReader lnr = new LineNumberReader(new InputStreamReader(is))) {
char[] buf = new char[8192];
int n, previousN = -1;
//Read will return at least one byte, no need to buffer more
while((n = lnr.read(buf)) != -1) {
previousN = n;
}
int ln = lnr.getLineNumber();
if (previousN == -1) {
//No data read at all, i.e file was empty
return 0;
} else {
char lastChar = buf[previousN - 1];
if (lastChar == '\n' || lastChar == '\r') {
//Ending with newline, deduct one
return ln;
}
}
//normal case, return line number + 1
return ln + 1;
}
}
직관적 인 결과를 원한다면 이것을 사용할 수 있습니다. wc -l
호환성을 원한다면 간단히 @ er.vikas 솔루션을 사용하지만 결과에 솔루션을 추가하지 않고 건너 뛰기를 다시 시도하십시오.
try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) {
while(lnr.skip(Long.MAX_VALUE) > 0){};
return lnr.getLineNumber();
}
Java 코드 내에서 Process 클래스를 사용하는 것은 어떻습니까? 그런 다음 명령의 출력을 읽습니다.
Process p = Runtime.getRuntime().exec("wc -l " + yourfilename);
p.waitFor();
BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
int lineCount = 0;
while ((line = b.readLine()) != null) {
System.out.println(line);
lineCount = Integer.parseInt(line);
}
그래도 시도해야합니다. 결과를 게시합니다.
인덱스 구조가 없으면 전체 파일을 읽을 수 없습니다. 그러나 한 줄씩 읽지 않고 정규식을 사용하여 모든 줄 종결자를 일치시키지 않고 최적화 할 수 있습니다.
Unix 기반 시스템의 wc
경우 명령 행 에서 명령을 사용하십시오 .
EOF에서 줄 바꿈 ( '\ n') 문자가없는 여러 줄 파일에 가장 최적화 된 코드입니다.
/**
*
* @param filename
* @return
* @throws IOException
*/
public static int countLines(String filename) throws IOException {
int count = 0;
boolean empty = true;
FileInputStream fis = null;
InputStream is = null;
try {
fis = new FileInputStream(filename);
is = new BufferedInputStream(fis);
byte[] c = new byte[1024];
int readChars = 0;
boolean isLine = false;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if ( c[i] == '\n' ) {
isLine = false;
++count;
}else if(!isLine && c[i] != '\n' && c[i] != '\r'){ //Case to handle line count where no New Line character present at EOF
isLine = true;
}
}
}
if(isLine){
++count;
}
}catch(IOException e){
e.printStackTrace();
}finally {
if(is != null){
is.close();
}
if(fis != null){
fis.close();
}
}
LOG.info("count: "+count);
return (count == 0 && !empty) ? 1 : count;
}
정규식 스캐너 :
public int getLineCount() {
Scanner fileScanner = null;
int lineCount = 0;
Pattern lineEndPattern = Pattern.compile("(?m)$");
try {
fileScanner = new Scanner(new File(filename)).useDelimiter(lineEndPattern);
while (fileScanner.hasNext()) {
fileScanner.next();
++lineCount;
}
}catch(FileNotFoundException e) {
e.printStackTrace();
return lineCount;
}
fileScanner.close();
return lineCount;
}
그것을 시계하지 않았습니다.
이것을 사용하면
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;
}
reader.getLineNumber의 리턴이 int이기 때문에 100K 행을 좋아하는 큰 수의 행으로 실행할 수 없습니다. 최대 행을 처리하려면 긴 유형의 데이터가 필요합니다.
int
약 2 억까지의 값을 저장할 수 있습니다. 20 억 줄 이상의 파일을로드하는 경우 오버플로 문제가 있습니다. 즉, 인덱싱되지 않은 텍스트 파일을 20 억 줄 이상으로로드하는 경우 다른 문제가있을 수 있습니다.