C# 从大文本文件中读取随机行
我有一个5000多行的文件。我想找到最有效的方法,在每次运行程序时从这些行中选择一行。我原本打算使用随机方法选择一个(那是在我知道有5000行之前)。我认为这样做可能效率不高,所以我想我应该阅读第一行,然后从顶部删除它并将其添加到底部。但似乎我必须阅读整个文件,并创建一个新文件从顶部删除 最有效的方法是什么:随机方法还是新文件方法C# 从大文本文件中读取随机行,c#,text-files,C#,Text Files,我有一个5000多行的文件。我想找到最有效的方法,在每次运行程序时从这些行中选择一行。我原本打算使用随机方法选择一个(那是在我知道有5000行之前)。我认为这样做可能效率不高,所以我想我应该阅读第一行,然后从顶部删除它并将其添加到底部。但似乎我必须阅读整个文件,并创建一个新文件从顶部删除 最有效的方法是什么:随机方法还是新文件方法 该程序将每5分钟运行一次,我使用的是c#4.5,我假设目标是从5000多行的文件中随机选择一行 试试这个: 使用File.ReadLines(File.count()
该程序将每5分钟运行一次,我使用的是c#4.5,我假设目标是从5000多行的文件中随机选择一行 试试这个:
编辑:如前所述,执行File.ReadLines(File).toArray()非常低效。在.NET 4.*中,可以直接访问文件的一行。例如,要获取第X行:
string line = File.ReadLines(FileName).Skip(X).First();
完整示例:
var fileName = @"C:\text.txt"
var file = File.ReadLines(fileName).ToList();
int count = file.Count();
Random rnd = new Random();
int skip = rnd.Next(0, count);
string line = file.Skip(skip).First();
Console.WriteLine(line);
以下是对问题的评论中提出的方法的快速实施:
// open the file
using(FileStream stream = File.OpenRead("yourfile.dat"))
{
// 1. index all offsets that are the beginning of a line
List<Long> lineOffsets = new List<Long>();
lineOffsets.Add(stream.Position); //the very first offset is a beginning of a line!
int ch;
while((ch = stream.ReadByte()) != -1) // "-1" denotes the end of the file
{
if(ch == '\n')
lineOffsets.Add(stream.Position);
}
// 2. read a random line
stream.Seek(0, SeekOrigin.Begin); // go back to the beginning of the file
// set the position of the stream to one the previously saved offsets
stream.Position = lineOffsets[new Random().Next(lineOffsets.Count)];
// read the whole line from the specified offset
using(StreamReader reader = new StreamReader(stream))
{
Console.WriteLine(reader.ReadLine());
}
}
//打开文件
使用(FileStream stream=File.OpenRead(“yourfile.dat”))
{
//1.索引作为直线起点的所有偏移
List lineoffset=新列表();
lineoffset.Add(stream.Position);//第一个偏移量是一行的开始!
int-ch;
而((ch=stream.ReadByte())!=-1)/“-1”表示文件的结尾
{
如果(ch='\n')
lineoffset.Add(stream.Position);
}
//2.随机读一行
stream.Seek(0,SeekOrigin.Begin);//返回文件的开头
//将流的位置设置为先前保存的偏移之一
stream.Position=lineoffset[new Random().Next(lineoffset.Count)];
//从指定的偏移量读取整行
使用(StreamReader=新StreamReader(stream))
{
Console.WriteLine(reader.ReadLine());
}
}
目前我附近没有任何VS,因此这是未经测试的。假设文件太大,您无法将其装入RAM。然后,您可能希望使用一种算法,该算法设计用于处理从未知、任意长度的列表中随机选取可能不适合内存的内容:
Random r = new Random();
int currentLine = 1;
string pick = null;
foreach (string line in File.ReadLines(filename))
{
if (r.Next(currentLine) == 0) {
pick = line;
}
++currentLine;
}
return pick;
在较高的水平上,储层取样遵循一个基本规则:每增加一条线,就有1/N的机会替换所有先前的线
这个算法有点不直观。在较高的级别上,它的工作原理是使第N行有1/N的机会替换当前选定的行。因此,第1行有100%的几率被选中,但之后被第2行取代的几率为50%
我发现以正确性证明的形式理解这个算法是最容易的。因此,一个简单的归纳证明:
1) 基本情况:通过检查,如果有一行,则算法有效。2) 如果算法适用于N-1行,则处理N行是有效的,因为:
3) 处理N行文件的N-1次迭代后,所有N-1行的可能性相同(概率为1/(N-1))。
4) 下一次迭代确保第N行的概率为1/N(因为这是算法明确指定的,并且是最终迭代),从而将所有前一行的概率降低为:
1/(N-1) * (1-(1/N))
1/(N-1) * (N/N-(1/N))
1/(N-1) * (N-1)/N
(1*(N-1)) / (N*(N-1))
1/N
如果您事先知道文件中有多少行,则此算法的开销比需要的要大,因为它总是读取整个文件。查找文件中的随机偏移量,然后向前扫描换行符。读取数据直到下一个换行符。在文件结束时采取预防措施。但是,如果这些线的长度差异很大,概率就不一样了。哦,5000不是那么多;-)将其拆分为50个文件,每个文件有100行,文件为随机编号0-50,行为随机编号0-99。尽管如此,每5分钟阅读5000行仍然不是一个大问题。。。效率不高,但不是真正的问题。如果这是应用程序的唯一问题,那么你很好:)文件总共有多大?正如@Lucastzesniewski所写,你应该在文件中寻找一个随机偏移量。但请记住,如果您的文件采用可变字符长度编码(如UTF-8),则此解决方案不起作用。效率是性能与成本的衡量标准,但您没有说明您感兴趣的性能方面或成本方面。如果你不能描述性能和成本,那么没有人能为你回答这个问题;如果你能同时衡量这两个方面,那么你就会知道答案。从目前评论中提出的所有问题来看,这将是最低效的解决方案。您对
File.ReadLines的两个调用都将读取整个文件(调用ToArray
会使步骤3变得非常懒惰)-除此之外:是的,您假设正确,因为这正是他所要求的,绝对正确。我将删除ToArray()方法调用。但是你也说得对,无论如何这并不是最有效的方法。但是你还是多次读取了文件:file.ReadAllLines
会更快,就像我不会执行file.ReadAllLines一样,因为这样会返回一个巨大的数组(这也是file.ReadLines().toArray()所做的)。使用FIle.readLines的要点是它返回一个IEnumerable。例如:我很清楚。。。根据硬件的不同,对于File.ReadLines(FileName).Skip(X).Take(1).First()
可以简化为File.ReadLines(FileName).Skip(X).First()
您的完整示例是将整个文件读入内存两次。没错,我已经编辑了代码。通过只读取一次文件,我的系统上的执行时间从~8毫秒变为~6毫秒。谢谢你也已经声明了两次var文件
,但是