如何循环并比较两个文本文件中的数百万个值?

时间:2011-09-21 09:54:29

标签: c# arrays performance data-structures loops

我有两个文本文件文件(TXT),其中包含超过200万个不同的文件名。我想遍历第一个文件中的所有名称,并找到第二个文本文件中也存在的名称。

我试过循环StreamReader,但需要花费很多时间。我也试过下面的代码,但它仍然需要太多时间。

StreamReader first = new StreamReader(path);
string strFirst = first.ReadToEnd();
string[] strarrFirst = strFirst.Split('\n');

 bool found = false;

StreamReader second = new StreamReader(path2);
string str = second.ReadToEnd();
string[] strarrSecond = str.Split('\n');

for (int j = 0; j < (strarrFirst.Length); j++)
{
          found = false;

    for (int i = 0; i < (strarrSecond .Length); i++)
    {
        if (strarrFirst[j] == strarrSecond[i])
        {
            found = true;
            break;
        }
    }

    if (!found)
    {
        Console.WriteLine(strarrFirst[j]);
    }
}

比较文件的好方法是什么?

3 个答案:

答案 0 :(得分:10)

这个怎么样:

var commonNames = File.ReadLines(path).Intersect(File.ReadLines(path2));

那是O(N + M)而不是你当前的解决方案,用第二个文件中的每个行测试第一个文件中的每个行 - O(N * M) )。

假设您正在使用.NET 4.否则,您可以使用File.ReadAllLines,但这会将整个文件读入内存。或者你可以自己编写相当于File.ReadLines的东西 - 它并不是非常难。

最终,当你摆脱当前代码中的O(N * M)问题时,你可能会被文件IO限制 - 没有太多方法可以解决这个问题。

编辑:对于.NET 2,首先让我们实现ReadLines

之类的东西
public static IEnumerable<string> ReadLines(string file)
{
    using (TextReader reader = File.OpenText(file))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

现在我们确实想要使用HashSet<T>,但这不是在.NET 2中 - 所以让我们使用Dictionary<TKey, TValue>代替:

Dictionary<string, string> map = new Dictionary<string, string>();
foreach (string line in ReadLines(path))
{
    map[line] = line;
}

List<string> intersection = new List<string>();
foreach (string line in ReadLines(path2))
{
    if (map.ContainsKey(line))
    {
        intersection.Add(line);
    }
}

答案 1 :(得分:1)

尝试这样的方法来加快速度......

            var path = string.Empty;
            var path2 = string.Empty;
            var strFirst = string.Empty;
            var str = string.Empty;
            var strarrFirst = new List<string>();
            var strarrSecond = new List<string>();

            using (var first = new StreamReader(path))
            {
                strFirst = first.ReadToEnd();
            }

            using (var second = new StreamReader(path2))
            {
                str = second.ReadToEnd();
            }


            strarrFirst.AddRange(strFirst.Split('\n'));

            strarrSecond.AddRange(str.Split('\n'));
            strarrSecond.Sort();

            foreach(var value in strarrFirst)
            {
                var found = strarrSecond.BinarySearch(value) >= 0;
                if (!found) Console.WriteLine(value);
            }

答案 2 :(得分:0)

为了好玩,我尝试了Jon Skeet方法并拥有:

    var guidArray = Enumerable.Range(0, 1000000).Select(x => Guid.NewGuid().ToString()).ToList();
        string path = "first.txt";
        File.WriteAllLines(path, guidArray);
        string path2 = "second.txt";
        File.WriteAllLines(path2, guidArray.Select(x=>DateTime.UtcNow.Ticks % 2 == 0 ? x : Guid.NewGuid().ToString()));

        var start = DateTime.Now;

        var commonNames = File.ReadLines(path).Intersect(File.ReadLines(path2)).ToList();

        Console.WriteLine((DateTime.Now - start).TotalMilliseconds);

        start = DateTime.Now;

        var lines = File.ReadAllLines(path);
        var hashset = new HashSet<string>(lines);

        var lines2 = File.ReadAllLines(path2);

        var result = lines2.Where(hashset.Contains).ToList();

        Console.WriteLine((DateTime.Now - start).TotalMilliseconds);
        Console.ReadKey();

并且Skeet的方法稍微快一点(1453.0831 vs 1488.0851,iDevForFun方法相当慢 - 12791.7316),所以我认为在图层下应该发生与我试图用hashset手动做同样的事情。