使用C#比较并获得2个CSV文件的差异

时间:2014-09-15 18:59:42

标签: c# .net csv

我每天都会阅读几次CSV文件。它大约300MB,每次我必须阅读它,与数据库中的现有数据进行比较,添加新数据,隐藏旧数据并更新现有数据。还有大量数据无法触及。

我可以访问所有新旧文件,我想将新文件与之前的文件进行比较,只需更新文件中的更改内容即可。我不知道该怎么做,我用C#来完成我的所有工作。可能最有问题的一件事是,前一字段中的一行可能位于第二个Feed中的另一个位置,即使它根本没有更新。如果可能的话,我也想避免这个问题。

任何想法都会有所帮助。

3 个答案:

答案 0 :(得分:2)

  • 使用其中一个existing CSV parsers
  • 将每一行解析为映射的对象
  • 覆盖您的对象的EqualsGetHashCode
  • 在内存中保留List<T>HashSet<T>,第一步是在没有内容的情况下初始化它们。
  • 在读取CSV文件中的每一行时,检查内存中集合(List,HashSet)中是否存在
  • 如果对象不存在于内存中集合中,请将其添加到集合中并插入数据库中。
  • 如果对象存在于您的内存中集合中,则忽略它(检查它将基于Equals和GetHashCode实现,然后它就像if(inMemoryCollection.Contains(currentRowObject))
  • 一样简单

我猜你有一个Windows服务定期从文件位置读取CSV文件。每次阅读新的CSV文件时,您都可以重复上述过程。这样,您将能够维护以前插入的对象的内存中集合并忽略它们,而不管它们在CSV文件中的位置。

如果您拥有为您的数据定义的主键,那么您可以使用Dictionary<T,T>,其中Key可以是唯一字段。这有助于您获得更多性能以进行比较,您可以忽略EqualsGetHashCode实施。

作为此过程的备份,您的数据库写入例程/存储过程应该以它首先检查的方式定义,如果表中已经存在记录,那么更新表,否则INSERT新记录。这将是UPSERT

请记住,如果您最终维护内存中的集合,请定期清除它,否则可能会导致内存不足异常。

答案 1 :(得分:0)

csv文件有多大?如果它的小试试以下

string [] File1Lines = File.ReadAllLines(pathOfFileA);
      string [] File2Lines = File.ReadAllLines(pathOfFileB);
      List<string> NewLines = new List<string>();
      for (int lineNum = 0; lineNo < File1Lines.Length; lineNo++)
      {
        if(!String.IsNullOrEmpty(File1Lines[lineNum]) 
 String.IsNullOrEmpty(File2Lines[lineNo]))
        {
          if(String.Compare(File1Lines[lineNo], File2Lines[lineNo]) != 0)
            NewLines.Add(File2Lines[lineNo]) ;
        }
        else if (!String.IsNullOrEmpty(File1Lines[lineNo]))
        {
        }
        else
        {
          NewLines.Add(File2Lines[lineNo]);
        }
      }
      if (NewLines.Count > 0)
      {
        File.WriteAllLines(newfilepath, NewLines);
      }

答案 2 :(得分:0)

好奇,你为什么要把旧文件与新文件进行比较?是不是SQL Server中旧文件的数据已经存在? (当你说数据库时,你的意思是SQL服务器吗?我假设是SQL服务器,因为你使用的是C#.net)

我的方法很简单:

  • 将新CSV文件加载到临时表
  • 使用存储过程来插入,更新和设置非活动文件

    public static void ProcessCSV(FileInfo file)
    {
        foreach (string line in ReturnLines(file))
        {
            //break the lines up and parse the values into parameters
            using (SqlConnection conn = new SqlConnection(connectionString))
            using (SqlCommand command = conn.CreateCommand())
            {
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "[dbo].sp_InsertToStaging";
    
                //some value from the string Line, you need to parse this from the string
                command.Parameters.Add("@id", SqlDbType.BigInt).Value = line["id"];
                command.Parameters.Add("@SomethingElse", SqlDbType.VarChar).Value = line["something_else"];
    
                //execute
                if (conn.State != ConnectionState.Open)
                    conn.Open();
    
                try
                {
                    command.ExecuteNonQuery();
                }
                catch (SqlException exc)
                {
                    //throw or do something
                }
            }
        }
    }
    
    public static IEnumerable<string> ReturnLines(FileInfo file)
    {
        using (FileStream stream = File.Open(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
        using (StreamReader reader = new StreamReader(stream))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }
    

现在您编写存储过程以插入,更新,根据ID设置非活动字段。如果特定Id的Field_x(main_table)!= Field_x(staging_table),您将知道是否更新了一行,等等。

以下是检测主表和登台表之间的更改和更新的方法。

/* SECTION: SET INACTIVE */
UPDATE main_table
SET IsActiveTag = 0
WHERE unique_identifier IN
    (
        SELECT a.unique_identifier
        FROM main_table AS a INNER JOIN staging_table AS b
        --inner join because you only want existing records
        ON a.unique_identifier = b.unique_identifier
        --detect any updates
        WHERE a.field1 <> b.field2
            OR a.field2 <> b.field2
            OR a.field3 <> b.field3
            --etc
    )

/* SECTION: INSERT UPDATED AND NEW */
INSERT INTO main_table
SELECT *
FROM staging_table AS b
LEFT JOIN
    (SELECT *
    FROM main_table
    --only get active records
    WHERE IsActiveTag = 1) AS a
ON b.unique_identifier = a.unique_identifier
--select only records available in staging table
WHERE a.unique_identifier IS NULL