Java의 파일 내 줄 수
대용량 데이터 파일을 사용합니다.이 파일의 행수만 알면 되는 경우가 있습니다.보통 파일을 열고 파일 끝에 도달할 때까지 한 줄씩 읽습니다.
좀 더 현명한 방법이 없을까 해서요
지금까지 발견된 것 중 가장 빠른 버전으로 readLines보다 약 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년 반 후):저는 자바 경험이 거의 없습니다만, 어쨌든 저는 이 코드를 이 코드와 비교해서 벤치마킹하려고 했습니다.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();
}
}
GB ).GB 'Y' ('Y')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을 반환합니다.이 문제를 해결하는 인정된 솔루션의 구현을 다음에 나타냅니다.끝NewLine을 사용하지 않으면 최종 판독을 제외한 모든 것이 낭비되지만 전체 기능에 비해 시간이 오래 걸리지 않습니다.
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();
}
}
Java-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;
}
위의 라인 카운트 방법을 테스트했습니다.시스템에서 테스트된 다양한 방법에 대한 관찰 결과를 다음에 나타냅니다.
파일 크기: 1.6 Gb 방법:
- 스캐너 사용방법: 약 35s
- Buffered Reader 사용 : 약 5s
- Java 8 : 5s 사용 약
- Line Number Reader 사용방법: 약 5s
게다가 Java8 어프로치는 매우 편리한 것 같습니다.
Files.lines(Paths.get(filePath), Charset.defaultCharset()).count()
[Return type : long]
오래된 질문인 것은 알지만, 받아들여진 솔루션이 제가 필요로 하는 것과 완전히 일치하지 않았습니다.그래서 (회선 피드만이 아니라) 다양한 회선 터미네이터를 받아들이고 (ISO-8859-n이 아닌) 지정된 문자 인코딩을 사용하도록 수정했습니다.All in One 메서드(해당하는 경우 리팩터):
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;
}
}
테스트 완료, 매우 고속.
스캐너를 사용한 간단한 방법
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: 의 줄바꿈 계산 방법은 정상이지만 마지막 줄이 줄바꿈으로 끝나지 않는 파일에 대해 직관적이지 않은 결과를 반환합니다.
LineNumberReader를 기반으로 한 And@er.vikas 솔루션은 행 수에 하나를 추가하면 마지막 행이 줄바꿈으로 끝나는 파일에 대해 직관적이지 않은 결과가 반환되었습니다.
그래서 저는 다음과 같이 취급하는 algo를 만들었습니다.
@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호환성, 단순한 use@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);
}
그래도 해봐야지.결과를 게시합니다.
이 재미있는 해결책은 실제로 효과가 있습니다!
public static int countLines(File input) throws IOException {
try (InputStream is = new FileInputStream(input)) {
int count = 1;
for (int aChar = 0; aChar != -1;aChar = is.read())
count += aChar == '\n' ? 1 : 0;
return count;
}
}
Line Number Reader에서는 몇 가지 다른 방법을 사용할 수 있는 것 같습니다.
나는 이렇게 했다:
int lines = 0;
FileReader input = new FileReader(fileLocation);
LineNumberReader count = new LineNumberReader(input);
String line = count.readLine();
if(count.ready())
{
while(line != null) {
lines = count.getLineNumber();
line = count.readLine();
}
lines+=1;
}
count.close();
System.out.println(lines);
또한 Java BufferedReader lines() 메서드를 사용하여 요소의 스트림을 반환하고 Stream count() 메서드를 사용하여 모든 요소를 카운트할 수 있습니다.그런 다음 출력에 1을 추가하면 텍스트 파일의 행 수를 얻을 수 있습니다.
예:
FileReader input = new FileReader(fileLocation);
LineNumberReader count = new LineNumberReader(input);
int lines = (int)count.lines().count() + 1;
count.close();
System.out.println(lines);
Unix에서는, 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이므로 큰 num 행으로 실행할 수 없습니다.최대 행을 처리하려면 긴 유형의 데이터가 필요합니다.
언급URL : https://stackoverflow.com/questions/453018/number-of-lines-in-a-file-in-java
'programing' 카테고리의 다른 글
| Vue warn: 구성 요소를 마운트하지 못했습니다. 템플릿 또는 렌더 함수가 정의되지 않았습니다. (0) | 2022.08.28 |
|---|---|
| 아직 Vue 템플릿 또는 렌더 함수가 정의되지 않았습니까? (0) | 2022.08.28 |
| Nuxt 유니버설 앱서버 측 Vuex 스토어는 사용자의 머신에 관계없이 요청 간에 캐시됩니다. (0) | 2022.08.28 |
| 다이내믹 어레이를 C의 구조체 내부에 포함하려면 어떻게 해야 합니까? (0) | 2022.08.28 |
| GDB 파손된 스택프레임 - 디버깅 방법 (0) | 2022.08.28 |
