有没有更好的方法来确定大 txt 文件(1-2 GB)中的行数? [复制]
Posted
技术标签:
【中文标题】有没有更好的方法来确定大 txt 文件(1-2 GB)中的行数? [复制]【英文标题】:Is there a better way to determine the number of lines in a large txt file(1-2 GB)? [duplicate] 【发布时间】:2016-04-01 23:26:52 【问题描述】:我正在尝试计算 txt 文件中的所有行,我使用的是 StreamReader
:
public int countLines(string path)
var watch = System.Diagnostics.Stopwatch.StartNew();
int nlines=0;
string line;
StreamReader file = new StreamReader(path);
while ((line = file.ReadLine()) != null)
nlines++;
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.Write(elapsedMs)
// elapsedMs = 3520 --- Tested with a 1.2 Mill txt
return nlines;
有没有更有效的方法来计算行数?
【问题讨论】:
这已经是最好的方法了。需要多长时间? 为避免分配然后丢弃一大堆字符串,调用file.Read()
并计算回车和/或换行字符的数量可能更有效。
如果您不需要文件内容(除了行数),您可以删除 string line
变量并执行 while (file.ReadLine() != null) nlines++;
@derpirscher 虽然可能更清楚意图,但它绝对不会影响最终速度。
您的代码本质上是计算指针前进到下一个 0×0A 的次数(因为这也涵盖了 0×0c 和 0×0A 的组合)。在增加计数的同时将其作为原始指针推进运行,看看这是否提高了 StreamReader 开销的效率。我对此感到生疏,所以我邀请评论。
【参考方案1】:
我只是在这里大声思考,但性能很可能受 I/O 限制而不是 CPU 限制。无论如何,我想知道是否将文件解释为文本可能会减慢速度,因为它必须在文件的编码和string
的本机编码之间进行转换。如果您知道编码是 ASCII 或与 ASCII 兼容,那么您可能只需计算一个值为 10 的字节出现的次数(这是换行符的字符代码)就可以逃脱。
如果你有以下情况:
FileStream fs = new FileStream("path.txt", FileMode.Open, FileAccess.Read, FileShare.None, 1024 * 1024);
long lineCount = 0;
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
do
bytesRead = fs.Read(buffer, 0, buffer.Length);
for (int i = 0; i < bytesRead; i++)
if (buffer[i] == '\n')
lineCount++;
while (bytesRead > 0);
我对 1.5GB 文本文件的基准测试结果,计时 10 次,取平均值:
StreamReader
接近,4.69 秒
File.ReadLines().Count()
接近,4.54 秒
FileStream
接近,1.46 秒
【讨论】:
您可以使用char
作为变量名,因为它是C# 保留关键字,也许@byte
或currentByte
会更好?您可能还会发现读取字节缓冲区的性能更高。无论哪种方式,+1 可以避免那些不必要的 string
分配。
@dreamlax 您假设新行表示为LF (\n) (10)
或CRLF (\r\n)
。换行定义为回车\r (13)
的情况呢?
我生成了一个包含 1M 行的文件,所有行都显示为“这是第 #n 行”。使用OP的方法,它在124ms内计算了1M。使用 ReadByte 方法,需要 220 毫秒。我用int newline = (int)Encoding.ASCII.GetBytes(Environment.NewLine)[0];
存储了比较值只是想把我的结果扔出去。
我已经更新了我的帖子,我得到了一个非常不同的结果:-)
赞成。使用citiesTour_400.txt
,我得到了 SR 3.754s 和 FS 2.751s。我敢肯定,如果我优化发布并可能禁用我的防病毒软件,它会更快。 :-)【参考方案2】:
您已经有了合适的解决方案,但您可以将所有代码简化为:
var lineCount = File.ReadLines(@"C:\MyHugeFile.txt").Count();
基准
我不确定dreamlax
是如何达到他的基准测试结果的,但这里有一些东西可以让任何人都可以在他们的机器上重现;你可以复制粘贴到 LINQPad 中。
首先让我们准备输入文件:
var filePath = @"c:\MyHugeFile.txt";
for (int counter = 0; counter < 5; counter++)
var lines = new string[30000000];
for (int i = 0; i < lines.Length; i++)
lines[i] = $"This is a line with a value of: i";
File.AppendAllLines(filePath, lines);
这应该会产生一个 1.5 亿行的文件,大约 6 GB。
现在让我们运行每个方法:
void Main()
var filePath = @"c:\MyHugeFile.txt";
// Make sure you clear windows cache!
UsingFileStream(filePath);
// Make sure you clear windows cache!
UsingStreamReaderLinq(filePath);
// Make sure you clear windows cache!
UsingStreamReader(filePath);
private void UsingFileStream(string path)
var sw = Stopwatch.StartNew();
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
long lineCount = 0;
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
do
bytesRead = fs.Read(buffer, 0, buffer.Length);
for (int i = 0; i < bytesRead; i++)
if (buffer[i] == '\n')
lineCount++;
while (bytesRead > 0);
Console.WriteLine("[FileStream] - Read: 0:n0 in 1", lineCount, sw.Elapsed);
private void UsingStreamReaderLinq(string path)
var sw = Stopwatch.StartNew();
var lineCount = File.ReadLines(path).Count();
Console.WriteLine("[StreamReader+LINQ] - Read: 0:n0 in 1", lineCount, sw.Elapsed);
private void UsingStreamReader(string path)
var sw = Stopwatch.StartNew();
long lineCount = 0;
string line;
using (var file = new StreamReader(path))
while ((line = file.ReadLine()) != null) lineCount++;
Console.WriteLine("[StreamReader] - Read: 0:n0 in 1", lineCount, sw.Elapsed);
结果:
[FileStream] - 读取:150,000,000 in 00:00:37.3397443
[StreamReader+LINQ] - 在 00:00:33.8842190 中读取:150,000,000
[StreamReader] - 在 00:00:34.2102178 中读取:150,000,000
更新
使用优化ON
运行会导致:
[FileStream] - 读取:150,000,000 in 00:00:18.1636374
[StreamReader+LINQ] - 读取:150,000,000 in 00:00:33.3173354
[StreamReader] - 读取:150,000,000 in 00:00:32.3530890
【讨论】:
@dreamlax 不会的!您将ReadLines
与ReadAllLines
混淆,前者返回IEnumerable<string>
。参考:***.com/questions/119559/…
比 StreamReader 方式慢一点(3619 毫秒)但还是谢谢 :)
@Brayan,对 IO 进行基准测试并不像运行两次代码并比较结果那么简单。特别是在处理磁盘时。至少您需要清除Windows Cached files
的内容,然后多次运行它们并取平均值。您可以使用 RAMMap 清除缓存,更多信息:***.com/questions/478340/…
@dreamlax,这次我刚刚用优化 ON
更新了结果,你的方法几乎快了 2 倍 :-) 我唯一的反对意见是缺乏对 carriage return (\r)
的支持
@MaYaN:确实,您的回答要安全得多。对于具有混合行尾的文件,我的可能会给出不同的结果。我经常处理 PostScript 文件,并且经常看到混合行结尾(嵌入文件可能有一个行结尾,而整个 PostScript 文件可能有另一个)。我还经常处理 Mac(尤其是旧的 Mac),并且不时遇到带有 \r
行结尾的文件,但这种情况很少见(而且越来越少)。以上是关于有没有更好的方法来确定大 txt 文件(1-2 GB)中的行数? [复制]的主要内容,如果未能解决你的问题,请参考以下文章