[java] Java 파일의 줄 수

나는 거대한 데이터 파일을 사용하며 때로는이 파일의 줄 수만 알아야합니다. 일반적으로 파일을 열고 파일 끝에 도달 할 때까지 한 줄씩 읽습니다.

더 똑똑한 방법이 있는지 궁금합니다.



답변

이것은 지금까지 찾은 가장 빠른 버전으로 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();
    }
}


답변

스트림을 사용할 수 있습니다.

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 방법 :

  1. 스캐너 사용 : 약 35 초
  2. BufferedReader 사용 : 약 5 초
  3. Java 8 사용 : 약 5 초
  4. LineNumberReader 사용 : 약 5 초

또한 Java8 접근법은 매우 편리합니다.

Files.lines(Paths.get(filePath), Charset.defaultCharset()).count()
[Return type : long]