2009-08-14 34 views
49

Tôi có một tệp lớn. Nó bao gồm khoảng 3.000-20.000 dòng. Làm thế nào tôi có thể nhận được tổng số dòng trong tệp bằng cách sử dụng Java?Làm cách nào để có thể đếm số lượng dòng trong một tệp theo cách hiệu quả?

+1

Đánh giá từ bình luận của bạn cho câu trả lời, từ mà bạn đang tìm kiếm là 'hiệu quả', không 'hiệu quả' . – AakashM

+0

Có, Bạn đang ở bên phải – firstthumb

+0

@Đầu tiên: Vui lòng không xóa nhận xét * sau * mọi người đã trả lời họ. Nó làm cho các chủ đề gây nhầm lẫn cho những người đến trễ để hiển thị. – Telemachus

Trả lời

84
BufferedReader reader = new BufferedReader(new FileReader("file.txt")); 
int lines = 0; 
while (reader.readLine() != null) lines++; 
reader.close(); 

Cập nhật: Để trả lời các câu hỏi hiệu suất lớn lên ở đây, tôi đã thực hiện một phép đo. Điều đầu tiên: 20.000 dòng là quá ít, để có được chương trình chạy trong một thời gian đáng chú ý. Tôi đã tạo một tệp văn bản với 5 triệu dòng. Giải pháp này (bắt đầu bằng java mà không có tham số như -server hoặc -XX-options) cần khoảng 11 giây trên hộp của tôi. Tương tự với wc -l (UNIX dòng lệnh-công cụ để đếm dòng), 11 giây. Giải pháp đọc từng ký tự đơn và tìm kiếm '\ n' cần 104 giây, gấp 9-10 lần.

+0

Bạn có ý nghĩa gì? Hiệu suất? Trong trường hợp đó bạn sẽ không có cách nào tốt hơn, bởi vì các dòng có thể có độ dài khác nhau, bạn sẽ phải đọc toàn bộ tệp, để đếm số dòng (wc cũng vậy). Nếu bạn nói về hiệu quả lập trình hơn tôi chắc chắn bạn có thể đặt nó trong một phương pháp tiện ích (hoặc một số thư viện phổ biến đã làm nó đã). – Mnementh

+0

@Firstthumb. Không hiệu quả, nhưng ai quan tâm. Anh ta chỉ đếm 20k dòng mà là khá nhỏ. Mã này nhận được phiếu bầu của tôi là đơn giản nhất. –

+0

hiệu suất của LineNumberReader vì nó mở rộng BufferedReader như thế nào? – Narayan

4

Đọc tệp thông qua và đếm số ký tự dòng mới. Một cách dễ dàng để đọc một tệp trong Java, một dòng tại một thời điểm, là lớp java.util.Scanner.

0

Đọc dòng tệp theo dòng và tăng bộ đếm cho mỗi dòng cho đến khi bạn đọc toàn bộ tệp.

29

sử dụng LineNumberReader

cái gì đó như

public static int countLines(File aFile) throws IOException { 
    LineNumberReader reader = null; 
    try { 
     reader = new LineNumberReader(new FileReader(aFile)); 
     while ((reader.readLine()) != null); 
     return reader.getLineNumber(); 
    } catch (Exception ex) { 
     return -1; 
    } finally { 
     if(reader != null) 
      reader.close(); 
    } 
} 
+2

Bạn có lẽ cũng cần phải đóng() người đọc. –

+0

yup, được thực hiện nhờ: D – Narayan

+2

bạn có thể muốn kiểm tra người đọc! = Null trong khối cuối cùng – dfa

-2

Người đọc đệm là overkill

Reader r = new FileReader("f.txt"); 

int count = 0; 
int nextchar = 0; 
while (nextchar != -1){ 
     nextchar = r.read(); 
     if (nextchar == Character.getNumericValue('\n')){ 
      count++; 
     } 
    } 

tìm kiếm của tôi cho một ví dụ đơn giản đã createde một thats thực sự khá nghèo. gọi đọc() repeadedly cho một nhân vật duy nhất là ít hơn tối ưu. xem here để biết các ví dụ và số đo.

+2

BufferedReader xử lý các dòng kết thúc khác nhau. Giải pháp của bạn bỏ qua các kết thúc dòng Mac ('\ r'). Điều đó có thể được. Dù sao, giải pháp của bạn không thực sự đọc từ tệp trong thời điểm này. Tôi nghĩ rằng bạn quên một dòng. – Mnementh

+5

Điều gì sẽ thay đổi nextchar ở đây? Nếu bạn định gọi read() trên mỗi lần lặp lại, tôi mạnh mẽ nghi ngờ rằng phương pháp BufferedReader sẽ * nhanh hơn nhiều * ... –

+0

đó là ý tưởng; -/Tôi muốn viết ví dụ đơn giản nhất có thể. Tôi tự hỏi sự khác biệt về tốc độ sẽ là gì? – NSherwin

2

Tất cả các câu trả lời trước đây được đề xuất để đọc mặc dù toàn bộ tệp và đếm số lượng dòng mới bạn tìm thấy khi thực hiện việc này. Bạn nhận xét một số là "không hiệu quả" nhưng đó là cách duy nhất bạn có thể làm điều đó. Một "dòng" là không có gì khác như một nhân vật đơn giản bên trong tập tin. Và để đếm nhân vật đó, bạn phải xem xét từng ký tự đơn trong tệp.

Tôi xin lỗi, nhưng bạn không có lựa chọn nào khác. :-)

2

Nếu câu trả lời đã đăng không đủ nhanh, có thể bạn sẽ phải tìm giải pháp cụ thể cho vấn đề cụ thể của mình.

Ví dụ: nếu các tệp văn bản này là nhật ký chỉ được nối vào và bạn thường xuyên cần biết số dòng trong đó bạn có thể tạo chỉ mục. Chỉ mục này sẽ chứa số dòng trong tệp, khi tệp được sửa đổi lần cuối và tệp lớn như thế nào. Điều này sẽ cho phép bạn tính toán lại số dòng trong tệp bằng cách bỏ qua tất cả các dòng bạn đã thấy và chỉ đọc các dòng mới.

+0

+1 đây có thể là một thuật toán trực tuyến phù hợp. – zeroin23

-1

Có lẽ giải pháp nhanh nhất trong Java thuần túy là đọc tệp dưới dạng byte bằng cách sử dụng Kênh NIO thành ByteBuffer lớn. Sau đó, sử dụng kiến ​​thức của bạn về (các) lược đồ mã hóa tệp đếm số CR và/hoặc byte được mã hóa theo quy ước phân tách đường có liên quan.

Chìa khóa để tối đa hóa thông lượng sẽ là:

  • chắc chắn rằng bạn đọc các tập tin trong khối lớn,
  • tránh sao chép các byte từ một bộ đệm khác,
  • tránh sao chép/chuyển đổi byte thành các ký tự và
  • tránh phân bổ các đối tượng để đại diện cho các dòng tệp.

Mã thực tế quá phức tạp để tôi viết khi đang di chuyển. Bên cạnh đó, OP không yêu cầu giải pháp nhanh nhất.

1

Hãy thử lệnh "wc" unix. Tôi không có nghĩa là sử dụng nó, tôi có nghĩa là tải về các nguồn và xem cách họ làm điều đó. Nó có thể trong c, nhưng bạn có thể dễ dàng chuyển đổi hành vi này sang java. Vấn đề của riêng bạn là giải thích vấn đề cr/lf kết thúc.

3

này nói về hiệu quả như nó có thể nhận được, đệm đọc nhị phân, không có chuyển đổi chuỗi,

FileInputStream stream = new FileInputStream("/tmp/test.txt"); 
byte[] buffer = new byte[8192]; 
int count = 0; 
int n; 
while ((n = stream.read(buffer)) > 0) { 
    for (int i = 0; i < n; i++) { 
     if (buffer[i] == '\n') count++; 
    } 
} 
stream.close(); 
System.out.println("Number of lines: " + count); 
2

Nhanh chóng và bẩn, nhưng nó không được công việc:

import java.io.*; 

public class Counter { 

    public final static void main(String[] args) throws IOException { 
     if (args.length > 0) { 
      File file = new File(args[0]); 
      System.out.println(countLines(file)); 
     } 
    } 

    public final static int countLines(File file) throws IOException { 
     ProcessBuilder builder = new ProcessBuilder("wc", "-l", file.getAbsolutePath()); 
     Process process = builder.start(); 
     InputStream in = process.getInputStream(); 
     LineNumberReader reader = new LineNumberReader(new InputStreamReader(in)); 
     String line = reader.readLine(); 
     if (line != null) { 
      return Integer.parseInt(line.trim().split(" ")[0]); 
     } else { 
      return -1; 
     } 
    } 

} 
+0

Một hiệu ứng phụ, giải pháp này không phải là nền tảng chéo. – Stephan

9

Tôi tìm thấy một số giải pháp cho điều này, nó có thể hữu ích cho bạn

Dưới đây là đoạn mã cho, đếm các dòng no.of từ tệp.

File file = new File("/mnt/sdcard/abc.txt"); 
    LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(file)); 
    lineNumberReader.skip(Long.MAX_VALUE); 
    int lines = lineNumberReader.getLineNumber(); 
    lineNumberReader.close(); 
+1

kết quả là các dòng 'đếm - 1' – MariuszS

+1

thực tế kết quả là' dòng + 1' –

+0

kết quả là getLineNumber() cộng 1 vì chỉ số dòng bắt đầu tại 0 –

3

Bạn có cần số dòng chính xác hoặc chỉ xấp xỉ của nó không? Tôi tình cờ xử lý các tệp lớn song song và thường tôi không cần phải biết số lượng dòng chính xác - tôi sau đó hoàn nguyên về lấy mẫu. Tách tệp thành mười khối 1MB và đếm các dòng trong mỗi đoạn, sau đó nhân nó với 10 và bạn sẽ nhận được xấp xỉ khá tốt về số lượng dòng.

2

Giải pháp này nhanh hơn khoảng 3,6 × so với câu trả lời được đánh giá cao nhất khi được kiểm tra trên một tệp có 13,8 triệu dòng. Nó chỉ đơn giản là đọc các byte vào một bộ đệm và đếm các ký tự \n. Bạn có thể chơi với kích thước bộ đệm, nhưng trên máy tính của tôi, bất cứ điều gì trên 8KB đã không làm cho mã nhanh hơn.

private int countLines(File file) throws IOException { 
    int lines = 0; 

    FileInputStream fis = new FileInputStream(file); 
    byte[] buffer = new byte[BUFFER_SIZE]; // BUFFER_SIZE = 8 * 1024 
    int read; 

    while ((read = fis.read(buffer)) != -1) { 
     for (int i = 0; i < read; i++) { 
      if (buffer[i] == '\n') lines++; 
     } 
    } 

    fis.close(); 

    return lines; 
} 
+0

Tôi tự hỏi, nếu sử dụng mẫu RegEx được biên dịch trước sẽ làm cho nó nhanh hơn hoặc chậm hơn.Những gì nó sẽ làm là làm việc với tất cả các kết thúc dòng, tôi tin. Và, tôi nghĩ rằng nó có thể làm cho nó nhanh hơn, quá. – ingyhere

+0

Một số giải pháp trên có thể tận dụng khả năng đệm, nếu lợi ích sẽ giúp ích. Ví dụ, "mới LineNumberReader (mới FileReader (theFilePathStr), 8096)" hoặc một cái gì đó. – ingyhere

+0

Hãy cẩn thận về mã hóa ký tự ... –

1

Bài đăng cũ, nhưng tôi có giải pháp hữu ích cho người tiếp theo. Tại sao không chỉ sử dụng độ dài tệp để biết tiến trình là gì? Tất nhiên, các dòng phải có kích thước gần như giống nhau, nhưng nó hoạt động rất tốt đối với các tệp lớn:

public static void main(String[] args) throws IOException { 
    File file = new File("yourfilehere"); 
    double fileSize = file.length(); 
    System.out.println("=======> File size = " + fileSize); 
    InputStream inputStream = new FileInputStream(file); 
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "iso-8859-1"); 
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 
    int totalRead = 0; 
    try { 
     while (bufferedReader.ready()) { 
      String line = bufferedReader.readLine(); 
      // LINE PROCESSING HERE 
      totalRead += line.length() + 1; // we add +1 byte for the newline char. 
      System.out.println("Progress ===> " + ((totalRead/fileSize) * 100) + " %"); 
     } 
    } finally { 
     bufferedReader.close(); 
    } 
} 

Nó cho phép xem tiến trình mà không đọc đầy đủ tệp. Tôi biết nó phụ thuộc vào rất nhiều yếu tố, nhưng tôi hy vọng nó sẽ hữu ích :).

[Ấn bản] Đây là phiên bản có thời gian ước tính. Tôi đặt một số SYSO để hiển thị tiến độ và ước tính. Tôi thấy rằng bạn có một lỗi ước tính thời gian tốt sau khi bạn đã xử lý đủ dòng (tôi thử với 10M dòng và sau 1% điều trị, ước tính thời gian chính xác là 95%). Tôi biết, một số giá trị phải được đặt trong biến. Mã này được viết nhanh nhưng có ích cho tôi. Hy vọng nó sẽ cho bạn quá :).

long startProcessLine = System.currentTimeMillis(); 
    int totalRead = 0; 
    long progressTime = 0; 
    double percent = 0; 
    int i = 0; 
    int j = 0; 
    int fullEstimation = 0; 
    try { 
     while (bufferedReader.ready()) { 
      String line = bufferedReader.readLine(); 
      totalRead += line.length() + 1; 
      progressTime = System.currentTimeMillis() - startProcessLine; 
      percent = (double) totalRead/fileSize * 100; 
      if ((percent > 1) && i % 10000 == 0) { 
       int estimation = (int) ((progressTime/percent) * (100 - percent)); 
       fullEstimation += progressTime + estimation; 
       j++; 
       System.out.print("Progress ===> " + percent + " %"); 
       System.out.print(" - current progress : " + (progressTime) + " milliseconds"); 
       System.out.print(" - Will be finished in ===> " + estimation + " milliseconds"); 
       System.out.println(" - estimated full time => " + (progressTime + estimation)); 
      } 
      i++; 
     } 
    } finally { 
     bufferedReader.close(); 
    } 
    System.out.println("Ended in " + (progressTime) + " seconds"); 
    System.out.println("Estimative average ===> " + (fullEstimation/j)); 
    System.out.println("Difference: " + ((((double) 100/(double) progressTime)) * (progressTime - (fullEstimation/j))) + "%"); 

Hãy cải thiện mã này nếu bạn nghĩ đó là giải pháp tốt.

0

Trong thử nghiệm của tôi, các câu trả lời khác mất ~ 150-300ms trên tệp dòng 118.5k. Sau đây cần 1ms nhưng chỉ gần đúng (báo cáo 117k dòng) và phụ thuộc vào mỗi dòng có kích thước tương tự.

private static void countSize(File file) { 
    long fileLength = file.length(); 
    BufferedReader reader = null; 
    try { 
    reader = new BufferedReader(new FileReader(file)); 
    //Skip header as it is of different size 
    reader.readLine(); 
    String text = reader.readLine(); 
    int lineLength = text.length(); 
    long lines = fileLength/lineLength; 
    System.out.println(lines); 
    } catch(IOException e) { 
    e.printStackTrace(); 
    } finally { 
    if(reader != null) { 
     try { 
     reader.close(); 
     } catch(IOException e) { 
     //no-op 
     } 
    } 
    } 
} 
13

Java 8 + có một cách rất tốt đẹp và ngắn sử dụng nio:

Path path = Paths.get("./big_file.txt"); 
long lineCount = Files.lines(path).count(); 
+0

giải pháp giường. chúng tôi có thể gặp sự cố với bộ ký tự – Mikhail

+1

bộ ký tự là UTF-8 theo mặc định –

Các vấn đề liên quan