답변:
이것은 지금까지 찾은 가장 빠른 버전으로 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파일의 개행 문자 수를 계산합니다. 이것은 모든 줄이 파일의 마지막 줄을 포함하여 줄 바꿈으로 끝나기 때문에 작동합니다. 모든 줄에는 빈 줄을 포함하여 줄 바꿈 문자가 있으므로 줄 바꿈 문자 수 == 파일의 줄 수입니다. 이제 lineNumberin 변수 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 억 줄 이상으로로드하는 경우 다른 문제가있을 수 있습니다.