กำหนดจำนวนบรรทัดภายในไฟล์ข้อความ


209

มีวิธีง่ายๆในการกำหนดจำนวนบรรทัดภายในไฟล์ข้อความโดยทางโปรแกรมหรือไม่?

คำตอบ:


396

การแก้ไขล่าช้าอย่างจริงจัง: หากคุณใช้. NET 4.0 หรือใหม่กว่า

Fileชั้นจะมีใหม่วิธีที่เฉื่อยชาระบุเส้นมากกว่าละโมบอ่านพวกเขาทั้งหมดลงในอาร์เรย์เช่นReadLines ReadAllLinesดังนั้นตอนนี้คุณสามารถมีทั้งประสิทธิภาพและความกระชับด้วย:

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

คำตอบเดิม

หากคุณไม่ใส่ใจเรื่องประสิทธิภาพคุณสามารถเขียน:

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

สำหรับวิธีที่มีประสิทธิภาพมากขึ้นคุณสามารถทำได้:

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

แก้ไข: เพื่อตอบคำถามเกี่ยวกับประสิทธิภาพ

เหตุผลที่ฉันพูดที่สองมีประสิทธิภาพมากขึ้นคือเกี่ยวกับการใช้หน่วยความจำไม่จำเป็นต้องเพิ่มความเร็ว คนแรกที่โหลดเนื้อหาทั้งหมดของไฟล์ลงในอาร์เรย์ซึ่งหมายความว่ามันจะต้องจัดสรรหน่วยความจำอย่างน้อยเท่าขนาดของไฟล์ ครั้งที่สองจะวนรอบทีละหนึ่งบรรทัดเท่านั้นดังนั้นจึงไม่จำเป็นต้องจัดสรรหน่วยความจำมากกว่าหนึ่งบรรทัดในแต่ละครั้ง นี่ไม่ใช่สิ่งสำคัญสำหรับไฟล์ขนาดเล็ก แต่สำหรับไฟล์ที่มีขนาดใหญ่กว่าอาจเป็นปัญหา (หากคุณลองและค้นหาจำนวนบรรทัดในไฟล์ 4GB บนระบบ 32 บิตตัวอย่างเช่นที่นั่นมีไม่เพียงพอ พื้นที่ที่อยู่โหมดผู้ใช้ในการจัดสรรอาร์เรย์ขนาดใหญ่นี้)

ในแง่ของความเร็วฉันไม่คิดว่ามันจะมีอะไรมากมาย เป็นไปได้ว่า ReadAllLines มีการเพิ่มประสิทธิภาพภายในบางอย่าง แต่ในทางกลับกันอาจต้องจัดสรรหน่วยความจำจำนวนมาก ฉันเดาว่า ReadAllLines อาจเร็วกว่าสำหรับไฟล์ขนาดเล็ก แต่ช้าลงอย่างมากสำหรับไฟล์ขนาดใหญ่ แม้ว่าวิธีเดียวที่จะบอกได้ก็คือการวัดด้วย Stopwatch หรือ Code Profiler


2
ข้อสังเกตเล็กน้อย: เนื่องจาก String เป็นชนิดอ้างอิงอาร์เรย์จะมีขนาดของจำนวนบรรทัด x ขนาดของตัวชี้ แต่คุณถูกต้องว่ามันยังคงต้องเก็บข้อความแต่ละบรรทัดเป็นวัตถุสตริงเดียว
Mike Dimmick

16
FYI: ในการที่จะทำเช่นนั้นReadLines().Count()คุณจะต้องเพิ่ม a using System.Linqไปยังของคุณ ดูเหมือนว่าไม่ใช่เรื่องง่ายที่จะต้องมีการเพิ่มนั่นคือเหตุผลที่ฉันพูดถึงมัน หากคุณใช้ Visual Studio อาจเป็นไปได้ว่าการเพิ่มนี้จะดำเนินการให้คุณโดยอัตโนมัติ
คลีออน

2
ฉันได้ทดสอบทั้งสองวิธีแล้ว "File.ReadLines.Count ()" v / s "reader.ReadLine ()" และ "reader.ReadLine ()" นั้นเร็วขึ้นเล็กน้อย แต่ก็เร็วกว่าเล็กน้อย "ReadAllLines" เป็นโยกซึ่งใช้เวลาสองเท่าและกินหน่วยความจำจำนวนมาก) นี่เป็นเพราะ "File.ReadLines.Count ()" และ "reader.ReadLine ()" เป็นตัวแจงนับที่อ่านไฟล์ทีละบรรทัดและไม่โหลดไฟล์ทั้งหมดในหน่วยความจำอ่านใน RAM อีกครั้ง
โยคี

9
ใช่ไม่มีใครทำงานกับไฟล์ 4GB + ได้ เราไม่เคยจัดการกับไฟล์บันทึกที่มีขนาดใหญ่อย่างแน่นอน โอ้เดี๋ยวก่อน
Greg Beech

2
หากคุณต้องการเห็นส่วนที่อยู่ภายในของ File.ReadLines () ไปที่นี่: System.IO.File.cs เมื่อคุณเจาะลึกการโอเวอร์โหลดมันจะพาคุณไปที่นี่: ReadLinesIterator.cs
Steve Kinyon


8

สิ่งนี้จะใช้หน่วยความจำน้อยลง แต่อาจใช้เวลานานกว่า

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

5

ถ้าง่ายคุณหมายถึงบรรทัดของรหัสที่ง่ายต่อการถอดรหัส แต่ไม่มีประสิทธิภาพต่อโอกาส?

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

นั่นอาจเป็นวิธีที่เร็วที่สุดในการรู้จำนวนบรรทัด

คุณสามารถทำได้ (ขึ้นอยู่กับว่าคุณกำลังบัฟเฟอร์อยู่หรือไม่)

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

ยังมีอีกหลายวิธี แต่อย่างใดอย่างหนึ่งข้างต้นอาจเป็นสิ่งที่คุณจะไปด้วย


3
ฉันยืนยันว่าวิธีนี้ไม่มีประสิทธิภาพมาก เพราะคุณกำลังอ่านไฟล์ทั้งหมดลงในหน่วยความจำและในอาเรย์สตริงไม่น้อย คุณไม่ต้องคัดลอกบัฟเฟอร์เมื่อใช้ ReadLine ดูคำตอบจาก @GregBeech ขอโทษที่ฝนตกในขบวนพาเหรดของคุณ
Mike Christian

2

คุณสามารถอ่านได้อย่างรวดเร็วและเพิ่มตัวนับเพียงใช้วนซ้ำเพื่อเพิ่มโดยไม่ทำอะไรกับข้อความ


3
นี่ควรเป็นความเห็นไม่ใช่คำตอบ
IamBatman

2

การอ่านไฟล์ในและโดยตัวของมันเองใช้เวลาพอสมควรการเก็บขยะผลลัพธ์เป็นปัญหาอื่นเมื่อคุณอ่านไฟล์ทั้งหมดเพื่อนับจำนวนอักขระขึ้นบรรทัดใหม่

เมื่อถึงจุดหนึ่งบางคนจะต้องอ่านตัวอักษรในไฟล์โดยไม่คำนึงว่าเฟรมเวิร์กนี้หรือว่าเป็นรหัสของคุณ ซึ่งหมายความว่าคุณต้องเปิดไฟล์และอ่านมันในหน่วยความจำหากไฟล์มีขนาดใหญ่ซึ่งอาจเป็นปัญหาเนื่องจากหน่วยความจำต้องรวบรวมขยะ

Nima Ara ทำการวิเคราะห์ที่ดีที่คุณอาจต้องคำนึงถึง

ต่อไปนี้เป็นวิธีแก้ไขปัญหาที่เสนอเนื่องจากจะอ่านอักขระได้ครั้งละ 4 ตัวนับจำนวนอักขระตัวป้อนบรรทัดและใช้ที่อยู่หน่วยความจำเดียวกันอีกครั้งเพื่อการเปรียบเทียบอักขระถัดไป

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;
}

ด้านบนคุณจะเห็นว่าบรรทัดมีการอ่านหนึ่งอักขระในแต่ละครั้งพร้อมกับเฟรมเวิร์กพื้นฐานที่คุณต้องอ่านอักขระทั้งหมดเพื่อดูตัวดึงข้อมูลบรรทัด

หากคุณโพรไฟล์มันเป็นอ่าวนิมาคุณจะเห็นว่านี่เป็นวิธีที่รวดเร็วและมีประสิทธิภาพในการทำสิ่งนี้


1

นับการขึ้นบรรทัดใหม่ ฉันเชื่อในยูนิโค้ดพวกเขายังคงเป็น 0x000D และ 0x000A ตามลำดับ ด้วยวิธีนี้คุณจะมีประสิทธิภาพหรือไม่มีประสิทธิภาพเท่าที่คุณต้องการและตัดสินใจว่าคุณต้องรับมือกับตัวละครทั้งสองหรือไม่


1

ตัวเลือกที่ทำงานได้และตัวเลือกที่ฉันใช้เป็นการส่วนตัวคือการเพิ่มส่วนหัวของคุณเองในบรรทัดแรกของไฟล์ ฉันทำสิ่งนี้ในรูปแบบโมเดลที่กำหนดเองสำหรับเกมของฉัน โดยทั่วไปฉันมีเครื่องมือที่ปรับไฟล์. obj ให้เหมาะสมกำจัดอึที่ไม่ต้องการแปลงให้เป็นเลย์เอาต์ที่ดีขึ้นจากนั้นเขียนจำนวนบรรทัดใบหน้าบรรทัดฐานจุดยอดและพื้นผิว UV บน บรรทัดแรก ข้อมูลนั้นจะถูกใช้โดยบัฟเฟอร์อาร์เรย์ต่าง ๆ เมื่อโหลดโมเดล

สิ่งนี้มีประโยชน์เพราะคุณต้องวนลูปไฟล์เพียงครั้งเดียวเพื่อทำการโหลดแทนที่จะนับหนึ่งครั้งเพื่อนับบรรทัดและอีกครั้งเพื่ออ่านข้อมูลลงในบัฟเฟอร์ที่คุณสร้างขึ้น


-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);
}         

5
-1: นี่จะช้าใช้หน่วยความจำมากและให้เวลากับ GC ยาก!
ya23

-2

คุณสามารถเปิดใช้งานโปรแกรมปฏิบัติการ" wc .exe" (มาพร้อมกับUnixUtilsและไม่จำเป็นต้องติดตั้ง) ให้ทำงานเป็นกระบวนการภายนอก รองรับวิธีการนับจำนวนบรรทัดที่แตกต่างกัน (เช่น unix vs mac vs windows)


1
ไม่มีวิธีนี้จะเร็วพอที่จะเป็นประโยชน์ ค่าโสหุ้ยของการเรียกใช้ไฟล์เรียกทำงานจะเพิ่มขึ้นเป็นสองเท่า
Krythic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.