我有一个包含5000多行的文件。我想找到每次运行程序时选择其中一行的最有效方法。我原本打算用随机方法选择一个(之前我知道有5000行)。认为可能效率低下,所以我认为我会看第一行,然后从顶部删除它并将其附加到底部。但似乎我必须阅读整个文件并创建一个要从顶部删除的新文件。
最有效的方法是:随机方法还是新文件方法?
该程序将每5分钟运行一次,我使用c#4.5
答案 0 :(得分:2)
在.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);
答案 1 :(得分:1)
让我们假设文件太大,以至于你无法承受将其装入RAM。然后,你会想要使用Reservoir Sampling,这是一种算法,用于处理从未知,任意长度的列表中随机挑选可能不适合内存的列表:
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)基本情况:通过检查,如果有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
如果您事先知道文件中有多少行,则此算法比必要的更昂贵,因为它总是读取整个文件。
答案 2 :(得分:0)
我认为目标是从5000多行的文件中随机选择一行。
试试这个:
编辑:正如所指出的那样,执行File.ReadLines(file).toArray()是非常低效的。
答案 3 :(得分:0)
以下是对问题评论中@LucasTrzesniewski提议的方法的快速实施:
// 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());
}
}
目前我附近没有任何VS,所以这是未经测试的。