Câu trả lời:
Đây là phiên bản nhanh nhất tôi tìm thấy cho đến nay, nhanh hơn khoảng 6 lần so với readLines. Trên tệp nhật ký 150 MB, việc này mất 0,35 giây, so với 2,40 giây khi sử dụng readLines (). Để giải trí, lệnh wc -l của linux mất 0,15 giây.
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();
}
}
EDIT, 9 năm rưỡi sau: Tôi thực tế không có kinh nghiệm về java, nhưng dù sao tôi cũng đã cố gắng đánh giá mã này theo LineNumberReader
giải pháp bên dưới vì nó làm phiền tôi rằng không ai làm điều đó. Dường như đối với các tệp lớn, giải pháp của tôi nhanh hơn. Mặc dù có vẻ như phải mất một vài lần chạy cho đến khi trình tối ưu hóa thực hiện công việc tốt. Tôi đã chơi một chút với mã và đã tạo ra một phiên bản mới luôn nhanh nhất:
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();
}
}
Điểm chuẩn nối lại cho tệp văn bản 1,3 GB, trục y trong vài giây. Tôi đã thực hiện 100 lần chạy với cùng một tệp và đo từng lần chạy với System.nanoTime()
. Bạn có thể thấy rằng countLinesOld
có một vài ngoại lệ, và countLinesNew
không có gì và trong khi nó chỉ nhanh hơn một chút, sự khác biệt có ý nghĩa thống kê. LineNumberReader
rõ ràng là chậm hơn.
Tôi đã thực hiện một giải pháp khác cho vấn đề này, tôi thấy nó hiệu quả hơn trong việc đếm hàng:
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
Trường của nó lineNumber
là một số nguyên ... Không phải nó chỉ bao bọc cho các tệp dài hơn Integer.MAX_VALUE? Tại sao phải bỏ qua một thời gian dài ở đây?
wc -l
đếm số lượng ký tự dòng mới trong tệp. Điều này hoạt động vì mỗi dòng được kết thúc bằng một dòng mới, bao gồm cả dòng cuối cùng trong một tệp. Mỗi dòng có một ký tự dòng mới, bao gồm các dòng trống, do đó số lượng ký tự dòng mới == số dòng trong một tệp. Bây giờ, lineNumber
biến trong FileNumberReader
cũng đại diện cho số ký tự dòng mới nhìn thấy. Nó bắt đầu từ 0, trước khi bất kỳ dòng mới nào được tìm thấy và được tăng lên với mỗi dòng char mới được nhìn thấy. Vì vậy, đừng thêm một vào số dòng xin vui lòng.
wc -l
báo cáo loại tệp này. Đồng thời xem stackoverflow.com/questions/729692/
wc -l
sẽ trả về 1. Tôi đã kết luận rằng tất cả các phương pháp đều có sai sót và đã thực hiện một phương pháp dựa trên cách tôi muốn nó hành xử, xem câu trả lời khác của tôi ở đây.
Câu trả lời được chấp nhận có một lỗi đối với các tệp đa dòng không kết thúc ở dòng mới. Một tệp một dòng kết thúc mà không có dòng mới sẽ trả về 1, nhưng một tệp hai dòng kết thúc mà không có dòng mới cũng sẽ trả về 1. Đây là một triển khai của giải pháp được chấp nhận để khắc phục điều này. Việc kiểm tra endWithoutNewLine gây lãng phí cho tất cả mọi thứ trừ lần đọc cuối cùng, nhưng nên là thời gian không đáng kể so với chức năng tổng thể.
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();
}
}
Với java-8, bạn có thể sử dụng luồng:
try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
Câu trả lời với số phương thức () ở trên đã cho tôi biết các dòng sai nếu một tệp không có dòng mới ở cuối tệp - không thể đếm được dòng cuối cùng trong tệp.
Phương pháp này hoạt động tốt hơn đối với tôi:
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
.
Tôi biết đây là một câu hỏi cũ, nhưng giải pháp được chấp nhận không hoàn toàn khớp với những gì tôi cần nó làm. Vì vậy, tôi đã tinh chỉnh nó để chấp nhận các đầu cuối dòng khác nhau (thay vì chỉ cung cấp dòng) và sử dụng mã hóa ký tự được chỉ định (thay vì ISO-8859- n ). Tất cả trong một phương thức (tái cấu trúc khi thích hợp):
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;
}
Giải pháp này có thể so sánh về tốc độ với giải pháp được chấp nhận, chậm hơn khoảng 4% trong các thử nghiệm của tôi (mặc dù các thử nghiệm thời gian trong Java nổi tiếng là không đáng tin cậy).
Tôi đã thử nghiệm các phương pháp trên để đếm các dòng và đây là những quan sát của tôi về các phương pháp khác nhau như được thử nghiệm trên hệ thống của tôi
Kích thước tệp: Phương pháp 1.6 Gb:
Hơn nữa, Cách tiếp cận Java8 có vẻ khá tiện dụng:
Files.lines(Paths.get(filePath), Charset.defaultCharset()).count()
[Return type : long]
/**
* 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();
}
}
Đã thử nghiệm trên JDK8_u31. Nhưng thực sự hiệu suất chậm so với phương pháp này:
/**
* 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;
}
}
Đã thử nghiệm và rất nhanh.
Stream<String> - Time consumed: 122796351 Stream<String> - Num lines: 109808 Method - Time consumed: 12838000 Method - Num lines: 1
Và số lượng dòng thậm chí còn sai
BufferedInputStream
khi bạn sẽ đọc vào bộ đệm của riêng bạn. Ngoài ra, ngay cả khi phương thức của bạn có thể có một lợi thế hiệu suất nhỏ, nó sẽ mất tính linh hoạt, vì nó không hỗ trợ các \r
đầu cuối dòng duy nhất (MacOS cũ) nữa và không hỗ trợ mọi mã hóa.
Một cách đơn giản bằng cách sử dụng Máy quét
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);
}
Tôi đã kết luận rằng wc -l
: phương pháp đếm dòng mới là tốt nhưng trả về kết quả không trực quan trên các tệp trong đó dòng cuối cùng không kết thúc bằng dòng mới.
Và giải pháp @ er.vikas dựa trên LineNumberReader nhưng việc thêm một vào số lượng dòng trả về kết quả không trực quan trên các tệp trong đó dòng cuối cùng kết thúc bằng dòng mới.
Do đó, tôi đã tạo ra một thuật toán xử lý như sau:
@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"));
}
Và nó trông như thế này:
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;
}
}
Nếu bạn muốn kết quả trực quan, bạn có thể sử dụng này. Nếu bạn chỉ muốn wc -l
tương thích, hãy sử dụng giải pháp @ er.vikas đơn giản, nhưng không thêm một kết quả vào kết quả và thử lại bỏ qua:
try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) {
while(lnr.skip(Long.MAX_VALUE) > 0){};
return lnr.getLineNumber();
}
Làm thế nào về việc sử dụng lớp Process từ bên trong mã Java? Và sau đó đọc đầu ra của lệnh.
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);
}
Cần phải thử nó mặc dù. Sẽ đăng kết quả.
Nếu bạn không có bất kỳ cấu trúc chỉ mục nào, bạn sẽ không thể đọc được tập tin hoàn chỉnh. Nhưng bạn có thể tối ưu hóa nó bằng cách tránh đọc từng dòng một và sử dụng biểu thức chính quy để khớp với tất cả các đầu cuối dòng.
Giải pháp hài hước này hoạt động thực sự tốt!
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;
}
}
Trên các hệ thống dựa trên Unix, sử dụng wc
lệnh trên dòng lệnh.
Cách duy nhất để biết có bao nhiêu dòng trong tệp là đếm chúng. Tất nhiên bạn có thể tạo một số liệu từ dữ liệu của mình, cung cấp cho bạn độ dài trung bình của một dòng và sau đó lấy kích thước tệp và chia số đó với avg. chiều dài nhưng điều đó sẽ không chính xác.
Mã được tối ưu hóa tốt nhất cho các tệp nhiều dòng không có ký tự dòng mới ('\ n') tại EOF.
/**
*
* @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;
}
Máy quét với regex:
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;
}
Không theo dõi nó.
nếu bạn sử dụng cái này
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;
}
bạn không thể chạy đến các hàng num lớn, thích các hàng 100K, vì trả về từ reader.getLineNumber là int. bạn cần loại dữ liệu dài để xử lý các hàng tối đa ..
int
có thể giữ các giá trị lên tới, xấp xỉ 2 tỷ. Nếu bạn đang tải một tệp có hơn 2 tỷ dòng, bạn có vấn đề tràn. Điều đó nói rằng, nếu bạn đang tải một tệp văn bản chưa được mã hóa với hơn hai tỷ dòng, bạn có thể gặp các vấn đề khác.