Xác định số lượng dòng trong một tệp văn bản


209

Có cách nào dễ dàng để xác định số lượng dòng trong tệp văn bản không?

Câu trả lời:


396

Chỉnh sửa muộn màng: Nếu bạn đang sử dụng .NET 4.0 trở lên

Các Filelớp học có một mới ReadLinesphương pháp mà lười biếng liệt kê dòng chứ không phải là tham lam đọc tất cả chúng vào một mảng như ReadAllLines. Vì vậy, bây giờ bạn có thể có cả hiệu quả và sự đồng nhất với:

var lineCount = File.ReadLines(@"C:\file.txt").Count();

Câu trả lời gốc

Nếu bạn không quá bận tâm về hiệu quả, bạn chỉ cần viết:

var lineCount = File.ReadAllLines(@"C:\file.txt").Length;

Đối với một phương pháp hiệu quả hơn bạn có thể làm:

var lineCount = 0;
using (var reader = File.OpenText(@"C:\file.txt"))
{
    while (reader.ReadLine() != null)
    {
        lineCount++;
    }
}

Chỉnh sửa: Trả lời các câu hỏi về hiệu quả

Lý do tôi nói thứ hai hiệu quả hơn là liên quan đến việc sử dụng bộ nhớ, không nhất thiết phải là tốc độ. Cái đầu tiên tải toàn bộ nội dung của tệp vào một mảng có nghĩa là nó phải phân bổ ít nhất nhiều bộ nhớ bằng kích thước của tệp. Cái thứ hai chỉ lặp lại một dòng tại một thời điểm để nó không bao giờ phải phân bổ nhiều hơn một bộ nhớ trong một dòng. Điều này không quan trọng đối với các tệp nhỏ, nhưng đối với các tệp lớn hơn thì đó có thể là một vấn đề (nếu bạn thử và tìm số dòng trong tệp 4GB trên hệ thống 32 bit, ví dụ, trong đó đơn giản là không đủ không gian địa chỉ chế độ người dùng để phân bổ một mảng lớn này).

Về tốc độ tôi không mong đợi sẽ có nhiều thứ trong đó. Có thể ReadAllLines có một số tối ưu hóa bên trong, nhưng mặt khác, nó có thể phải phân bổ một khối lớn bộ nhớ. Tôi đoán rằng ReadAllLines có thể nhanh hơn đối với các tệp nhỏ, nhưng chậm hơn đáng kể đối với các tệp lớn; mặc dù cách duy nhất để nói là đo nó bằng Đồng hồ bấm giờ hoặc trình lược tả mã.


2
Lưu ý nhỏ: vì Chuỗi là loại tham chiếu, mảng sẽ là kích thước của số dòng x kích thước của một con trỏ, nhưng bạn chính xác rằng nó vẫn cần lưu trữ văn bản, mỗi dòng dưới dạng một đối tượng Chuỗi.
Mike Dimmick

15
FYI: Để làm được điều đó, ReadLines().Count()bạn sẽ cần thêm một phần using System.Linqbao gồm. Có vẻ như không trực quan để yêu cầu bổ sung, vì vậy đó là lý do tại sao tôi đề cập đến nó. Nếu bạn đang sử dụng Visual Studio, có thể phần bổ sung này được thực hiện tự động cho bạn.
Hạt nhân

2
Tôi đã thử cả hai cách tiếp cận, "File.ReadLines.Count ()" v / s "reader.ReadLine ()" và "reader.ReadLine ()" nhanh hơn một chút nhưng nhanh hơn rất ít. "ReadAllLines" lỏng hơn, mất gấp đôi thời gian và tốn rất nhiều bộ nhớ). Điều này là do "File.ReadLines.Count ()" và "reader.ReadLine ()" là một liệt kê đọc từng dòng tệp và không tải lại toàn bộ tệp trong bộ nhớ đọc lại RAM.
Yogee

9
Vâng, không ai từng làm việc với các tập tin 4GB +. Chúng tôi chắc chắn không bao giờ đối phó với các tệp nhật ký lớn. Oh, đợi đã.
Greg Beech

2
Nếu bạn muốn xem phần bên trong của File.ReadLines () tại đây: System.IO.File.cs Khi bạn đi sâu vào tình trạng quá tải, bạn sẽ phải đến đây: ReadLinesIterator.cs
Steve Kinyon

12

Dễ nhất:

int lines = File.ReadAllLines("myfile").Length;

8

Điều này sẽ sử dụng ít bộ nhớ hơn, nhưng có thể mất nhiều thời gian hơn

int count = 0;
string line;
TextReader reader = new StreamReader("file.txt");
while ((line = reader.ReadLine()) != null)
{
  count++;
}
reader.Close();

5

Nếu dễ dàng, bạn có nghĩa là một dòng mã dễ giải mã nhưng cơ hội không hiệu quả?

string[] lines = System.IO.File.RealAllLines($filename);
int cnt = lines.Count();

Đó có lẽ là cách nhanh nhất để biết có bao nhiêu dòng.

Bạn cũng có thể làm (tùy thuộc vào việc bạn có đệm nó không)

#for large files
while (...reads into buffer){
string[] lines = Regex.Split(buffer,System.Enviorment.NewLine);
}

Có rất nhiều cách khác nhưng một trong những cách trên có lẽ là những gì bạn sẽ đi với.


3
Tôi cho rằng phương pháp này rất kém hiệu quả; bởi vì, bạn đang đọc toàn bộ tập tin vào bộ nhớ và vào một chuỗi chuỗi, không hơn không kém. Bạn không phải sao chép bộ đệm khi sử dụng ReadLine. Xem câu trả lời từ @GregB336. Xin lỗi để mưa trên cuộc diễu hành của bạn.
Mike Christian

2

Bạn có thể nhanh chóng đọc nó và tăng bộ đếm, chỉ cần sử dụng vòng lặp để tăng, không làm gì với văn bản.


3
Đây phải là một bình luận, không phải là một câu trả lời.
IamBatman

2

Việc đọc một tệp trong chính nó mất một thời gian, rác thu thập kết quả là một vấn đề khác khi bạn đọc toàn bộ tệp chỉ để đếm (các) ký tự dòng mới,

Tại một số điểm, ai đó sẽ phải đọc các ký tự trong tệp, bất kể đây là khung hoặc nếu đó là mã của bạn. Điều này có nghĩa là bạn phải mở tệp và đọc nó vào bộ nhớ nếu tệp lớn, điều này sẽ có khả năng là một vấn đề vì bộ nhớ cần phải được thu gom rác.

Nima Ara đã thực hiện một phân tích hay mà bạn có thể cân nhắc

Đây là giải pháp được đề xuất, vì nó đọc 4 ký tự cùng một lúc, đếm ký tự nguồn cấp dữ liệu và sử dụng lại cùng một địa chỉ bộ nhớ để so sánh ký tự tiếp theo.

private const char CR = '\r';  
private const char LF = '\n';  
private const char NULL = (char)0;

public static long CountLinesMaybe(Stream stream)  
{
    Ensure.NotNull(stream, nameof(stream));

    var lineCount = 0L;

    var byteBuffer = new byte[1024 * 1024];
    const int BytesAtTheTime = 4;
    var detectedEOL = NULL;
    var currentChar = NULL;

    int bytesRead;
    while ((bytesRead = stream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
    {
        var i = 0;
        for (; i <= bytesRead - BytesAtTheTime; i += BytesAtTheTime)
        {
            currentChar = (char)byteBuffer[i];

            if (detectedEOL != NULL)
            {
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 1];
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 2];
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 3];
                if (currentChar == detectedEOL) { lineCount++; }
            }
            else
            {
                if (currentChar == LF || currentChar == CR)
                {
                    detectedEOL = currentChar;
                    lineCount++;
                }
                i -= BytesAtTheTime - 1;
            }
        }

        for (; i < bytesRead; i++)
        {
            currentChar = (char)byteBuffer[i];

            if (detectedEOL != NULL)
            {
                if (currentChar == detectedEOL) { lineCount++; }
            }
            else
            {
                if (currentChar == LF || currentChar == CR)
                {
                    detectedEOL = currentChar;
                    lineCount++;
                }
            }
        }
    }

    if (currentChar != LF && currentChar != CR && currentChar != NULL)
    {
        lineCount++;
    }
    return lineCount;
}

Ở trên, bạn có thể thấy rằng một dòng được đọc một ký tự cùng một lúc bởi khung bên dưới khi bạn cần đọc tất cả các ký tự để xem nguồn cấp dữ liệu.

Nếu bạn mô tả nó như đã thực hiện thì Nima bạn sẽ thấy rằng đây là một cách khá nhanh và hiệu quả để làm việc này.


1

đếm lợi nhuận vận chuyển / nguồn cấp dữ liệu. Tôi tin vào unicode họ vẫn là 0x000D và 0x000A tương ứng. theo cách đó bạn có thể hiệu quả hoặc không hiệu quả như bạn muốn, và quyết định xem bạn có phải đối phó với cả hai nhân vật hay không


1

Một tùy chọn khả thi, và một tùy chọn mà tôi đã sử dụng cá nhân, sẽ là thêm tiêu đề của riêng bạn vào dòng đầu tiên của tệp. Tôi đã làm điều này cho một định dạng mô hình tùy chỉnh cho trò chơi của tôi. Về cơ bản, tôi có một công cụ tối ưu hóa các tệp .obj của mình, loại bỏ những thứ nhảm nhí mà tôi không cần, chuyển đổi chúng thành một bố cục tốt hơn và sau đó ghi tổng số đường, mặt, chuẩn, đỉnh và UV kết cấu trên dòng đầu tiên Dữ liệu đó sau đó được sử dụng bởi các bộ đệm mảng khác nhau khi mô hình được tải.

Điều này cũng hữu ích vì bạn chỉ cần lặp qua tệp một lần để tải nó vào, thay vì một lần để đếm các dòng và một lần nữa để đọc dữ liệu vào bộ đệm đã tạo của bạn.


-1
try {
    string path = args[0];
    FileStream fh = new FileStream(path, FileMode.Open, FileAccess.Read);
    int i;
    string s = "";
    while ((i = fh.ReadByte()) != -1)
        s = s + (char)i;

    //its for reading number of paragraphs
    int count = 0;
    for (int j = 0; j < s.Length - 1; j++) {
            if (s.Substring(j, 1) == "\n")
                count++;
    }

    Console.WriteLine("The total searches were :" + count);

    fh.Close();

} catch(Exception ex) {
    Console.WriteLine(ex.Message);
}         

4
-1: đây sẽ là SLOW, tiêu tốn rất nhiều bộ nhớ và cung cấp cho GC thời gian khó khăn!
ya23

-2

Bạn có thể khởi chạy tệp thực thi " wc .exe" (đi kèm với UnixUtils và không cần cài đặt) chạy như một quy trình bên ngoài. Nó hỗ trợ các phương thức đếm dòng khác nhau (như unix vs mac vs windows).


Không có cách nào điều này sẽ đủ nhanh để hữu ích. Chi phí chung của việc chỉ gọi thực thi sẽ gấp đôi (cường điệu rõ ràng là hiển nhiên) như một vòng lặp tăng dần.
Krythic
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.