65.9K
CodeProject 正在变化。 阅读更多。
Home

CSV过滤器

2009 年 9 月 27 日

CPOL

2分钟阅读

viewsIcon

47641

downloadIcon

1150

如何读取、过滤和写入你的 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_1COL_2COL_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 日:初始发布
© . All rights reserved.