确定文本文件中的行数


209

有没有一种简便的方法可以以编程方式确定文本文件中的行数?

Answers:


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

编辑:回应有关效率的问题

我说第二个效率更高的原因是关于内存使用情况,不一定是速度。第一个将文件的全部内容加载到一个数组中,这意味着它必须至少分配与文件大小相同的内存。第二个仅一次循环一行,因此它永远不必一次分配多于一行的内存。对于小文件来说,这并不是很重要,但是对于大文件来说,这可能是个问题(例如,如果您尝试在32位系统上查找4GB文件中的行数,那么根本不够用)用户模式地址空间来分配这么大的数组)。

就速度而言,我不希望其中有很多。ReadAllLines可能在内部进行了一些优化,但另一方面,它可能不得不分配大量的内存。我猜想对于小文件,ReadAllLines可能会更快,但是对于大文件,ReadAllLines可能会慢得多。尽管唯一的方法是使用秒表或代码分析器对其进行测量。


2
温馨提示:由于String是引用类型,因此数组的大小将是行数的大小x指针的大小,但是您是正确的,它仍然需要存储文本,每一行都作为单个String对象存储。
Mike Dimmick

16
仅供参考:要执行此操作,ReadLines().Count()您需要using System.Linq在包含中添加一个。要求添加它似乎很不直观,所以这就是我提到它的原因。如果您使用的是Visual Studio,则可能会自动为您完成此添加。
Nucleon

2
我已经测试了这两种方法,“ File.ReadLines.Count()”与“ reader.ReadLine()”和“ reader.ReadLine()”的速度稍快一些,但是速度却很小。“ ReadAllLines”较为宽松,需要花费两倍的时间并占用大量内存。这是因为“ File.ReadLines.Count()”和“ reader.ReadLine()”是一个枚举器,它逐行读取文件,并且不将整个文件加载到内存中,而是再次在RAM中读取它。
Yogee 2014年

9
是的,没人能使用4GB以上的文件。我们当然永远不会处理那么大的日志文件。等一下。
格雷格·比奇

2
如果您想查看File.ReadLines()的内部信息,请访问: System.IO.File.cs 当您深入研究重载时,它将带您到这里:ReadLinesIterator.cs
Steve Kinyon

12

最简单的:

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

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的答案。很抱歉下雨游行。
迈克·克里斯汀

2

您可以快速读入它,并增加一个计数器,只需使用循环来增加,而对文本不执行任何操作。


3
这应该是评论,而不是答案。
IamBatman'3

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

在上方可以看到,底层框架一次也读取一行字符,因为您需要读取所有字符才能看到换行符。

如果将它描述为完成的Nima海湾,您会发现这是一种相当快速有效的方法。


1

计算回车/换行符。我相信在unicode中它们分别仍然是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带来困难!
2013年

-2

您可以启动作为外部进程运行的“ wc .exe”可执行文件(UnixUtils附带,不需要安装)。它支持不同的行数计算方法(例如UNIX,Mac和Windows)。


1
这根本不可能足够快而有用。仅调用可执行文件的开销将是单个递增循环的两倍(明显的夸张是明显的)。
Krythic '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.