如何在C#中从另一个csv文件中删除一个csv文件的内容

时间:2014-05-29 15:56:44

标签: c# csv

我有2个csv文件,file1.csv和file2.csv。每个文件中的某些行将是相同的。我希望创建一个基于file2.csv的第三个csv文件,但是从file1.csv中删除了任何行。实际上我希望从file2.csv中减去file1.csv,忽略file1中不存在于file2中的任何行。 我知道我可以使用streamreader来读取file2.csv中的每一行并在file1.csv中搜索它。如果file1.csv中不存在,我可以将其写入file3.csv。但是,文件非常大(超过30000行),我相信这将需要大量的处理时间。 我怀疑可能有更好的方法将每个csv加载到数组,然后对它们执行简单的减法函数以获得所需的结果。我要感谢对代码或方法的一些帮助,我应该解决这个问题。

文件的示例内容:

file1.csv

dt97861.jpg,149954,c1714ee1,\folder1\folderA\,
dt97862.jpg,149955,c1714ee0,\folder1\folderA\,
dt97863.jpg,59368,cd23f223,\folder2\folderA\,
dt97864.jpg,57881,0835be4a,\folder2\folderB\,
dt97865.jpg,57882,0835be4b,\folder2\folderB\,

file2.csv

dt97862.jpg,149955,c1714ee0,\folder1\folderA\,
dt97863.jpg,59368,cd23f223,\folder2\folderA\,
dt97864.jpg,57881,0835be4a,\folder2\folderB\,
dt97865.jpg,57882,0835be4b,\folder2\folderB\,
dt97866.jpg,57883,0835be4c,\folder2\folderB\,
dt97867.jpg,57884,0835be4d,\folder3\folderA\,
dt97868.jpg,57885,0835be4e,\folder3\folderA\,

我要求的结果是:

file3.csv

dt97866.jpg,57883,0835be4c,\folder2\folderB\,
dt97867.jpg,57884,0835be4d,\folder3\folderA\,
dt97868.jpg,57885,0835be4e,\folder3\folderA\,

编辑: 在下面的帮助下,我找到了以下解决方案,我相信它很优雅:

public static IEnumerable<string> ReadFile(string path)
        {
            string line;

            using (var reader = File.OpenText(path))
                while ((line = reader.ReadLine()) != null)
                    yield return line;
        }

然后:

var file2 = ReadFile(file2FilePath);
var file1 = ReadFile(file1FilePath);
var file3 = file2.Except(file1);
File.WriteAllLines(file3FilePath, file3);

3 个答案:

答案 0 :(得分:1)

虽然这可能不是最佳方法,但它是我过去使用的方法。这有点肮脏,但是......

  1. 将两个CSV文件导入数据表(因此,如果您打算使用excel类型格式,您将有两个数据表 - 我个人更喜欢closed xml,否则只需使用普通文件读/写 - 我的示例使用常规读/写)
  2. 将数据从数据表移动到列表中(我的示例假定使用逗号分隔值,每行一个。)
  3. 在列表和合并之间查找唯一值
  4. 将合并的列表导出到csv文件
  5. * [实际处理代码后编辑的步骤]

    根据Bit的请求,我添加了一个使用来自Some Random Website的示例数据的示例 - 这是针对.NET 3.5在VS2008中编写的,但它应该在3.5+上运行。我将us-500复制到2个版本中,原始版本和修改后的1行创建了一个独特的测试值。该项目的目标是x86平台。我使用了新的Windows窗体进行测试

    using System.Data;
    using System.Data.OleDb;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace TestSandbox
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                var file1 = new DataTable();
                var file2 = new DataTable();
    
                InitializeComponent();
                //Gets data from csv file, select allows for filtering
                using (var conn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\;Extended Properties=""text;HDR=Yes;FMT=Delimited"";"))
                {
                    conn.Open();
                    using (var adapter = new OleDbDataAdapter(@"select * from [us-500.csv]", conn))
                    {
                        adapter.Fill(file1);
                    }
                }
    
                using (var conn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\;Extended Properties=""text;HDR=Yes;FMT=Delimited"";"))
                {
                    conn.Open();
                    using (var adapter = new OleDbDataAdapter(@"select * from [us-500-2.csv]", conn))
                    {
                        adapter.Fill(file2);
                    }
                }
                //Moves datatable information to lists for comparison
                var file1List = (from DataRow row in file1.Rows select row.ItemArray.Select(field => field.ToString()).ToArray() into fields select string.Join(",", fields)).ToList();
                var file2List = (from DataRow row in file2.Rows select row.ItemArray.Select(field => field.ToString()).ToArray() into fields select string.Join(",", fields)).ToList();
                //Adds all data from file2 into file1 list, except for data that already exists in file1
                file1List.AddRange(file2List.Except(file1List));
                //Exports all results to c:\results.csv
                File.WriteAllLines(@"C:\Results.csv", file1List.ToArray());
            }
        }
    }
    

    *注意:在查看代码之后,直接导入列表看起来会更有效率,但我现在不会这样做,因为它不会过于复杂。

答案 1 :(得分:1)

假设该行完全相同,您可以将这两个文件读入两个IEnumerable<string>并使用IEnumerable.Except<T>进行提取。无论顺序如何,这都会产生相同的结果〜

示例:

var file1 = new List<string>{
        @"dt97861.jpg,149954,c1714ee1,\folder1\folderA\,",
        @"dt97862.jpg,149955,c1714ee0,\folder1\folderA\,",
        @"dt97863.jpg,59368,cd23f223,\folder2\folderA\,",
        @"dt97864.jpg,57881,0835be4a,\folder2\folderB\,",
        @"dt97865.jpg,57882,0835be4b,\folder2\folderB\,",
    };

var file2 = new List<string>{
        @"dt97862.jpg,149955,c1714ee0,\folder1\folderA\,",
        @"dt97863.jpg,59368,cd23f223,\folder2\folderA\,",
        @"dt97864.jpg,57881,0835be4a,\folder2\folderB\,",
        @"dt97865.jpg,57882,0835be4b,\folder2\folderB\,",
        @"dt97866.jpg,57883,0835be4c,\folder2\folderB\,",
        @"dt97867.jpg,57884,0835be4d,\folder3\folderA\,",
        @"dt97868.jpg,57885,0835be4e,\folder3\folderA\,",
    };

file2.Except(file1).Dump();

输出:

dt97866.jpg,57883,0835be4c,\folder2\folderB\, 
dt97867.jpg,57884,0835be4d,\folder3\folderA\, 
dt97868.jpg,57885,0835be4e,\folder3\folderA\, 

以下是将任何文件加载到IEnumerable<string>的功能。别忘了using System.IO;

public static IEnumerable<string> ReadFile(string path)
{
    string line;

    using(var reader = File.OpenText(path))
        while((line = reader.ReadLine()) != null)
            yield return line;
}

将结果写入文件:

 //using System.IO; is required
File.WriteAllLines("file3.csv", file2.Except(file1))

备注:File.WriteAllLines将创建或覆盖该文件。

答案 2 :(得分:0)

步骤1.使用System.IO,我们将使用FileStream读取两个文件,并使用StreamWriter创建第三个文件。

步骤2.使用FileStream读取文件#1。例如

using (var FS = new System.IO.FileStream(file1, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { ...<insert next steps in here>...}

步骤3.嵌套另一个FileStream以读取文件#2。此流将被多次读取,因此如果您可以将较小的文件放在嵌套的这一部分中,那么它是最好的。您可以通过在跳转到这些循环之前检查文件的大小来执行此操作。

步骤4.从我们最大的文件File#1中读取一行,然后我们将它与File#2中的所有行进行顺序比较。如果找到匹配项,则将布尔值设置为TRUE,表示在文件#2中找到匹配的行。

步骤5.一旦我们在文件#2的末尾,检查布尔值的真/假条件。如果为false,请将我们从文件#1读取的字符串保存到文件#3中。这是你的输出文件。

步骤6.将文件#2的流指针重置到文件的开头,例如FS.Seek(0,System.IO.SeekOrigin.Begin)

步骤7.从步骤4开始重复,直到我们到达文件#1的末尾。文件#3的内容应仅代表文件#1中不属于文件#2的唯一条目