从大文本文件

时间:2015-09-01 09:54:46

标签: c# text-files

我有一个包含5000多行的文件。我想找到每次运行程序时选择其中一行的最有效方法。我原本打算用随机方法选择一个(之前我知道有5000行)。认为可能效率低下,所以我认为我会看第一行,然后从顶部删除它并将其附加到底部。但似乎我必须阅读整个文件并创建一个要从顶部删除的新文件。

最有效的方法是:随机方法还是新文件方法?

该程序将每5分钟运行一次,我使用c#4.5

4 个答案:

答案 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多行的文件中随机选择一行。

试试这个:

  1. 使用File.ReadLines(file).Count()获取行数。
  2. 使用行计数作为上限生成随机数。
  3. 使用File.ReadLines(file)执行文件的延迟读取。
  4. 使用随机数从此数组中选择一行。
  5. 编辑:正如所指出的那样,执行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,所以这是未经测试的。