【问题标题】:C# removing duplicate from large text files [closed]C#从大型文本文件中删除重复项[关闭]
【发布时间】:2020-01-01 13:12:26
【问题描述】:

我有一个 C# 应用程序,除其他外,它需要删除重复的行,并将该行出现次数的计数附加到行的末尾。

文件可能非常大,从我的角度来看,我不能假设文件大小有任何限制。

对我来说,处理这个文件的最佳方式似乎是逐行处理。

删除重复项很好 - 我有以下内容(来自这个问题 Remove Duplicate Lines From Text File?):

using (TextReader reader = File.OpenText(newFilePath))
using (TextWriter writer = File.CreateText(aggregateFilePathBase))
{
    string currentLine;
    var previousLines = new HashSet<string>();              

    while ((currentLine = reader.ReadLine()) != null)
    {
        if (previousLines.Add(currentLine))
        {
            writer.WriteLine(currentLine);
        }
        else
            duplicateArray.Add(currentLine);
    }
}

我的问题是我可以轻松识别和删除重复项,但附加计数证明是有问题的。如您所见,我有一个数组,其中包含所有重复项的列表。然后我可以使用它来获取重复项并生成新行,如下所示:

if (duplicateArray.Count() > 0)
{               
    var duplicateGroups = duplicateArray.GroupBy(x => x);
    foreach (var duplicate in duplicateGroups)
    {
        var duplicateCount = duplicate.Count() + 1;
        var newLine = duplicate.First() + "," + duplicateCount;                 
    }
}

问题是将计数写入文件。 我可以重新运行读/写过程,但我的问题是,基于对 150m 行文件的一些计算,此写入将需要 30 小时以上。所以在这种情况下,删除重复项需要 60 个小时。

谁能推荐一个更快的方法。

我假设将整个文件读入内存并进行查找和替换不是一种选择,因为文件太大而无法加载到内存中...

编辑: 从更多考虑这一点,我想我也可能会遇到 HashSet 的问题,因为这将有效地反映文件的大小,因此将限制为 2GB - 这是正确的吗?

【问题讨论】:

  • 可能在创建期间将占位符计数“00000”写入文件,并记住该计数在文件中的位置,以便您可以返回并使用 Seek() 更新它。只要占位符计数有足够的位数来保存最大计数,它就应该起作用。如果需要,最终计数可以有前导零,或者您可以根据需要用空格覆盖额外的计数 - 但无论哪种方式,您都必须在更新时写入正确数量的字符。
  • 基于 map/reduce 的方法怎么样?比如,将文件分成可消化的块并减少成对 [line, numberMatches]?
  • @Peter Duniho 你能告诉我你为什么搁置这个问题吗?在我看来,这个问题非常清楚。你能准确地告诉它有什么问题吗?
  • @EJoshuaS 你能告诉我你为什么搁置这个问题吗?在我看来,这个问题非常清楚。你能准确地告诉它有什么问题吗?
  • @Sangwin Gawande 你能告诉我你为什么搁置这个问题吗?在我看来,这个问题非常清楚。你能准确地告诉它有什么问题吗?

标签: c# file


【解决方案1】:

SQLite 非常适合这个。将每一行作为一个值插入数据库中,然后使用计数执行group by 查询以返回包含不同行的记录集和它们出现的次数。在单个事务中执行所有插入结果非常快。

using System;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;

...

using (var myconnection = new SQLiteConnection("Data Source=MyDatabase.sqlite;PRAGMA journal_mode=WAL;"))
using (var fileLineReader = new StreamReader(newFilePath))
{
    myconnection.Open();
    var cmd = myconnection.CreateCommand();

    cmd.CommandText = "drop table if exists myfile";
    cmd.ExecuteNonQuery();

    cmd.CommandText = "create table myfile (value varchar(2147483647))";
    cmd.ExecuteNonQuery();

    using (var transaction = myconnection.BeginTransaction())
    {
        cmd.CommandText = "insert into myfile (value) values (@value)";

        string line;
        while ((line = fileLineReader.ReadLine()) != null)
        {
            cmd.Parameters.Clear();
            cmd.Parameters.AddWithValue("@value", line);
            cmd.ExecuteNonQuery();
        }
        transaction.Commit();
    }

    cmd.CommandText = "select value,count(*) as count from myfile group by value";

    using (TextWriter writer = File.CreateText(aggregateFilePathBase))
    {
        foreach (IDataRecord record in cmd.ExecuteReader())
        {
            writer.WriteLine(record["value"] + "," + record["count"]);
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    相关资源
    最近更新 更多