如何在c#中从另一个csv文件中删除内容
本文关键字:文件 删除 csv 另一个 | 更新日期: 2023-09-27 18:05:59
我有2个csv文件,file1.csv和file2.csv。每个文件中的一些行是相同的。我希望创建第三个csv文件,基于file2.csv,但与任何行存在于file1.csv从它删除。实际上,我希望从file2.csv中减去file1.csv,忽略file1中存在的不在file2中的任何行。我知道我可以使用流阅读器读取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);
假设这一行完全相同,您可以将两个文件读入两个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
将创建或覆盖文件
虽然这可能不是最好的方法,但这是我过去使用过的方法。这是一个有点肮脏的hack,但是…
- 将两个CSV文件导入到一个数据表中(这样您将有两个数据表-如果您计划使用excel类型格式,我个人更喜欢封闭的xml,否则只需使用普通文件读/写-我的示例使用常规读/写)
- 将数据表中的数据移动到列表中(我的示例假设逗号分隔的值,每行一个)
- 查找列表之间的唯一值并合并
- 将合并列表导出为csv文件
*[编辑代码后的步骤]
对于来自Bit的请求,我添加了一个使用来自一些随机网站的样本数据的示例-这是在VS2008中针对。net 3.5编写的,但它应该在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。使用系统。我们将使用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。从最大的文件文件#1中读取一行,然后将其与文件#2中的所有行进行顺序比较。如果找到匹配,将布尔值设置为TRUE,表示在文件#2中找到了匹配行。
第5步。一旦我们在文件#2的末尾,检查布尔值的真/假条件。如果为false,则将从文件#1读取的字符串保存到文件#3中。这是你的输出文件。
步骤6。将文件#2的流指针重置到文件的开头,例如FS。查找(0,System.IO.SeekOrigin.Begin)
步骤7。重复步骤4,直到到达文件#1的末尾。文件#3的内容应该只表示文件#1中不属于文件#2的唯一条目