CSV过滤器






4.72/5 (8投票s)
如何读取、过滤和写入你的 CSV 文件

引言
在本文中,你将学习如何读取一个 CSV(逗号分隔值)文件,并将其重新写入另一个文件,应用你选择的基本过滤器。
我写这篇文章的原因是我在桌面上有一些旧的、冗长的联系人列表,我想整理它,删除重复项,并将其转换为 CSV 文件。当然,我首先需要使用 Microsoft Word 将分号替换为逗号,然后在每行末尾插入特殊字符 ^p
,以确保我有一个包含两列的 CSV 文件格式。
背景
要理解本文,你需要熟悉 IO 流和 ADO 的基本知识。
Using the Code
首先,我们使用 BackgroundWorker
组件读取源 CSV 文件,并确保我们没有做任何可能导致跨线程崩溃的愚蠢事情。
我们创建一个 StreamReader
并逐行读取源文件,并根据指定的分隔符字符对其进行拆分,并查看生成的标记是否与预期的匹配,如果是,则在 DataTable
对象中创建一个行:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
DataTable dt;
StreamReader sr;
StreamWriter sw;
string[] tokens={String.Empty};
//Reading Source
OpenFile:
try
{
sr = new StreamReader(strSource);
}
catch (IOException)
{
DialogResult dr = MessageBox.Show("Source file in use!",
"Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
if (dr == DialogResult.Retry)
goto OpenFile;
else
return;
}
dt = new DataTable("CSV_SOURCE");
for (int x = 1; x <= (int)numTokens.Value; x++)
dt.Columns.Add("COL_"+x.ToString(), typeof(string));
backgroundWorker1.ReportProgress(0, "Reading source file...");
while (!sr.EndOfStream)
{
try
{
tokens = sr.ReadLine().Split(strSourceSeperator.ToCharArray()[0]);
if (tokens.Length == (int)numTokens.Value)
dt.Rows.Add(tokens);
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
sr.Close();
}
我们的 DataTable
对象列在一个简单的循环中创建,并命名为 COL_1
、COL_2
、COL_3
... 等,具体取决于用户指定的标记数量以及在源文件中找到的标记数量
dt = new DataTable("CSV_SOURCE");
for (int x = 1; x <= (int)numTokens.Value; x++)
dt.Columns.Add("COL_"+x.ToString(), typeof(string));
请注意,在启动线程时,我们必须创建一组类全局变量,以存储来自界面控件的输入,以确保线程安全
string strFilter, strSourceSeperator, strTargetSeperator;
private void btnFilter_Click(object sender, EventArgs e)
{
strFilter = txtFilter.Text;
strSourceSeperator = txtSourceSeperator.Text;
strTargetSeperator = txtTargetSeperator.Text;
backgroundWorker1.RunWorkerAsync();
}
现在我们删除重复项
//Removing Duplicates
if (chUnique.Checked)
dt = dsHelper.SelectDistinct("DISTINCT_CSV_SOURCE", dt, "COL_1");
实际上,我在上面的代码段中做了一些愚蠢的事情,你最好通过创建一个额外的类全局 bool
变量并将 checkbox
控制状态分配给它来修复它。
对于 SelectDistinct
方法,你需要将这个辅助类包含在你的解决方案中
public class DataSetHelper
{
public DataSet ds;
public DataSetHelper(ref DataSet DataSet)
{
ds = DataSet;
}
public DataSetHelper()
{
ds = null;
}
private bool ColumnEqual(object A, object B)
{
// Compares two values to see if they are equal. Also compares DBNULL.Value.
// Note: If your DataTable contains object fields, then you must extend this
// function to handle them in a meaningful way if you intend to group on them.
if (A == DBNull.Value && B == DBNull.Value) // both are DBNull.Value
return true;
if (A == DBNull.Value || B == DBNull.Value) // only one is DBNull.Value
return false;
return (A.Equals(B)); // value type standard comparison
}
public DataTable SelectDistinct
(string TableName, DataTable SourceTable, string FieldName)
{
DataTable dt = new DataTable(TableName);
dt.Columns.Add(FieldName, SourceTable.Columns[FieldName].DataType);
object LastValue = null;
foreach (DataRow dr in SourceTable.Select("", FieldName))
{
if (LastValue == null || !(ColumnEqual(LastValue, dr[FieldName])))
{
LastValue = dr[FieldName];
dt.Rows.Add(new object[] { LastValue });
}
}
if (ds != null)
ds.Tables.Add(dt);
return dt;
}
}
最后,我们创建一个 StreamWriter
对象,遍历我们先前创建的 DataTable
对象,逐行逐列地获取其数据行,包含新的分隔符字符(或保留原始字符),并关闭流。
//Writing Target
SaveFile:
try
{
sw = new StreamWriter(strTarget);
}
catch (IOException)
{
DialogResult dr = MessageBox.Show("Target file in use!",
"Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
if (dr == DialogResult.Retry)
goto SaveFile;
else
return;
}
backgroundWorker1.ReportProgress(50, "Writing to target file...");
string tmpLine;
foreach(DataRow dr in dt.Rows)
{
try
{
foreach (DataColumn dc in dt.Columns)
{
tmpLine = dr[dc].ToString();
//Filtering
switch(iFilter)
{
case 0:
if (tmpLine.Contains(strFilter))
sw.Write(tmpLine);
break;
case 1:
if (!tmpLine.Contains(strFilter))
sw.Write(tmpLine);
break;
}
if (!dc.ColumnName.EndsWith("_"+ dt.Columns.Count.ToString()))
sw.Write(strTargetSeperator);
}
sw.Write("\r\n");
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
sw.Close();
为了过滤结果,我们使用了以下基本的验证步骤
tmpLine = dr[dc].ToString();
//Filtering
switch(iFilter)
{
case 0:
if (tmpLine.Contains(strFilter))
sw.Write(tmpLine);
break;
case 1:
if (!tmpLine.Contains(strFilter))
sw.Write(tmpLine);
break;
}
历史
- 2009 年 9 月 27 日:初始发布