【问题标题】:C# Reading CSV to DataTable and Invoke Rows/ColumnsC# 读取 CSV 到 DataTable 并调用行/列
【发布时间】:2018-03-23 14:37:47
【问题描述】:

我目前正在做一个小项目,但遇到了一个我目前无法解决的问题......

我有多个要读取的“.CSV”文件,它们都有相同的数据,只是值不同。

Header1;Value1;Info1
Header2;Value2;Info2
Header3;Value3;Info3

在阅读第一个文件时,我需要创建标题。问题是它们不是按列拆分而是按行拆分(如您在上面的 Header1-Header3 中看到的)。

然后它需要读取值 1 - 值 3(它们列在第 2 列中),除此之外,我需要创建另一个 Header -> Header4,其中包含始终放置在列中的“Info2”数据3 和第 2 行(第 3 列的其他值我可以忽略)。

所以第一个文件之后的结果应该是这样的:

Header1;Header2;Header3;Header4;
Value1;Value2;Value3;Info2;

在多个文件之后,它应该是这样的:

Header1;Header2;Header3;Header4;
Value1;Value2;Value3;Value4;
Value1b;Value2b;Value3b;Value4b;
Value1c;Value2c;Value3c;Value4c;

我用 OleDB 尝试过,但我得到了错误“缺少 ISAM”,我无法修复。我使用的代码如下:

public DataTable ReadCsv(string fileName)
    {
        DataTable dt = new DataTable("Data");
       /* using (OleDbConnection cn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"" + 
            Path.GetDirectoryName(fileName) + "\";Extendet Properties ='text;HDR=yes;FMT=Delimited(,)';"))
        */
        using (OleDbConnection cn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
            Path.GetDirectoryName(fileName) + ";Extendet Properties ='text;HDR=yes;FMT=Delimited(,)';"))
        {
            using(OleDbCommand cmd = new OleDbCommand(string.Format("select *from [{0}]", new FileInfo(fileName).Name,cn)))
            {
                cn.Open();
                using(OleDbDataAdapter adapter = new OleDbDataAdapter(cmd))
                {
                    adapter.Fill(dt);
                }
            }
        }


        return dt;
    }

我做的另一个尝试是使用 StreamReader。但是标头在错误的位置,我不知道如何更改此+为每个文件执行此操作。我尝试的代码如下:

  public static DataTable ReadCsvFilee(string path)
    {  

        DataTable oDataTable = new DataTable();
        var fileNames = Directory.GetFiles(path);
        foreach (var fileName in fileNames)

        {

            //initialising a StreamReader type variable and will pass the file location
            StreamReader oStreamReader = new StreamReader(fileName);

            // CONTROLS WHETHER WE SKIP A ROW OR NOT
            int RowCount = 0;
            // CONTROLS WHETHER WE CREATE COLUMNS OR NOT
            bool hasColumns = false;
            string[] ColumnNames = null;
            string[] oStreamDataValues = null;
            //using while loop read the stream data till end
            while (!oStreamReader.EndOfStream)
            { 

                String oStreamRowData = oStreamReader.ReadLine().Trim();
                if (oStreamRowData.Length > 0)
                { 

                    oStreamDataValues = oStreamRowData.Split(';');
                    //Bcoz the first row contains column names, we will poluate 
                    //the column name by
                    //reading the first row and RowCount-0 will be true only once
                    // CHANGE TO CHECK FOR COLUMNS CREATED                      
                    if (!hasColumns)
                    {
                        ColumnNames = oStreamRowData.Split(';');

                        //using foreach looping through all the column names
                        foreach (string csvcolumn in ColumnNames)
                        {
                            DataColumn oDataColumn = new DataColumn(csvcolumn.ToUpper(), typeof(string));

                            //setting the default value of empty.string to newly created column
                            oDataColumn.DefaultValue = string.Empty;

                            //adding the newly created column to the table
                            oDataTable.Columns.Add(oDataColumn);
                        }
                        // SET COLUMNS CREATED
                        hasColumns = true;
                        // SET RowCount TO 0 SO WE KNOW TO SKIP COLUMNS LINE
                        RowCount = 0;
                    }
                    else
                    {
                        // IF RowCount IS 0 THEN SKIP COLUMN LINE
                        if (RowCount++ == 0) continue;
                        //creates a new DataRow with the same schema as of the oDataTable            
                        DataRow oDataRow = oDataTable.NewRow();

                        //using foreach looping through all the column names
                        for (int i = 0; i < ColumnNames.Length; i++)
                        {
                            oDataRow[ColumnNames[i]] = oStreamDataValues[i] == null ? string.Empty : oStreamDataValues[i].ToString();
                        }

                        //adding the newly created row with data to the oDataTable       
                        oDataTable.Rows.Add(oDataRow);
                    }

                }
            }
            //close the oStreamReader object
            oStreamReader.Close();
            //release all the resources used by the oStreamReader object
            oStreamReader.Dispose();
        }
            return oDataTable;
        }

感谢所有愿意提供帮助的人。感谢您阅读本文!

此致

【问题讨论】:

  • 您不需要驱动程序来读取 CSV 文件,在最简单的形式中,它们只是带有分隔符的文本文件。您可以一次读取一行并将其拆分。不过,您所描述的不是 CSV 文件。怎么会有人猜测 Value4 用于不同的领域?
  • 如果标题和值没有混淆,您可以像包含 3 个字段一样读取文件,按 first/header 字段分组并将该键值用作字段名称,组值作为项目。请解释Header4:Value4 和忽略值背后的逻辑。这是一种可能只是看起来像 CSV 的格式吗?
  • 哦,这是我的错!值 4 不像值 1-3 只是我想阅读的另一个信息。对不起,我应该称之为“Info1”。我不需要来自第 3 列的任何信息,只需要这个信息“value4”其他我不介意的信息。让我在我的问题中解决这个问题所以这只是 CSV 本身中没有标题的另一个值。所以我需要在我的 DataTable 中为它创建一个新的 Header,称为 Header4

标签: c# csv datatable import-csv csvtotable


【解决方案1】:

如果我没听错的话,这里有一个严格的解析:

string OpenAndParse(string filename, bool firstFile=false)
{
    var lines = File.ReadAllLines(filename);

    var parsed = lines.Select(l => l.Split(';')).ToArray();

    var header = $"{parsed[0][0]};{parsed[1][0]};{parsed[2][0]};{parsed[1][0]}\n";
    var data   = $"{parsed[0][1]};{parsed[1][1]};{parsed[2][1]};{parsed[1][2]}\n";

    return firstFile
    ? $"{header}{data}"
    : $"{data}";
}

返回的位置 - 如果是第一个文件:

Header1;Header2;Header3;Header2
Value1;Value2;Value3;Value4

如果不是第一个文件:

Value1;Value2;Value3;Value4

如果我是正确的,剩下的就是针对文件列表文件运行它并将结果加入输出文件中。

编辑:针对目录:

void ProcessFiles(string folderName, string outputFileName)
{
    bool firstFile = true;
    foreach (var f in Directory.GetFiles(folderName))
    {
        File.AppendAllText(outputFileName, OpenAndParse(f, firstFile));
        firstFile = false;
    }
}

注意:我错过了您想要一个 DataTable 而不是输出文件。然后您可以简单地创建一个列表并将结果放入该列表中,使该列表成为您的数据表的数据源(那么为什么要在其中使用分号?可能您所需要的只是简单地将数组值附加到列表中)。

【讨论】:

  • 谢谢!你所做的假设是完全正确的!我认为这可行。我目前在测试它时遇到问题(对不起,我对编码有点陌生)在您的代码中,您将文本添加到“文件”中,有没有办法将其添加到“数据表”中? “$”是干什么用的?在我的代码中,它说这个标志是“意外的”,我得到的其他错误是“标题/数据”从未使用过
  • 正如我在“笔记”中指出的那样,我真的很想念你想要一个数据表。列表可以是 DataTable 的数据源。我会试着为你组装一个例子。
  • 哦,我没看过!谢谢你,我真的很感激你的帮助! =)
【解决方案2】:

(添加为另一个答案只是为了使其整洁)

void ProcessMyFiles(string folderName)
{
    List<MyData> d = new List<MyData>();
    var files = Directory.GetFiles(folderName);
    foreach (var file in files)
    {
        OpenAndParse(file, d);
    }

    string[] headers = GetHeaders(files[0]);
    DataGridView dgv = new DataGridView {Dock=DockStyle.Fill};
    dgv.DataSource = d;
    dgv.ColumnAdded += (sender, e) => {e.Column.HeaderText = headers[e.Column.Index];};

    Form f = new Form();
    f.Controls.Add(dgv);
    f.Show();
}

string[] GetHeaders(string filename)
{
    var lines = File.ReadAllLines(filename);
    var parsed = lines.Select(l => l.Split(';')).ToArray();
    return new string[] { parsed[0][0], parsed[1][0], parsed[2][0], parsed[1][0] };
}

void OpenAndParse(string filename, List<MyData> d)
{
    var lines = File.ReadAllLines(filename);
    var parsed = lines.Select(l => l.Split(';')).ToArray();
    var data = new MyData
    {
        Col1 = parsed[0][1],
        Col2 = parsed[1][1],
        Col3 = parsed[2][1],
        Col4 = parsed[1][2]
    };
    d.Add(data);
}

public class MyData
{
    public string Col1 { get; set; }
    public string Col2 { get; set; }
    public string Col3 { get; set; }
    public string Col4 { get; set; }
}

【讨论】:

  • 我真的没想到会有这么多帮助!非常感谢!这绝对解决了我的问题!周末愉快!非常感谢
【解决方案3】:

我不知道这是否是最好的方法。但在你的情况下,我会做的是在读取所有文件时以传统方式重写 CSV,然后创建一个包含创建的新 CSV 的流。

它看起来像这样:

     var csv = new StringBuilder();
            csv.AppendLine("Header1;Header2;Header3;Header4");
            foreach (var item in file)
            {
                var newLine = string.Format("{0},{1},{2},{3}", item.value1, item.value2, item.value3, item.value4);
                csv.AppendLine(newLine);
            }

            //Create Stream
            MemoryStream stream = new MemoryStream();
            StreamReader reader = new StreamReader(stream);

            //Fill your data table here with your values

希望这会有所帮助。

【讨论】:

  • 感谢您的宝贵时间!我也想过,但是我必须阅读多达一千个这样的文件并且总是重写它不是最好的选项/减慢进程不是吗?嗯,没有办法只使用第 2 列并调用它(而不是按列拆分行),然后只读取“第 3 列,第 2 行”并将其放在 Header4 下?
猜你喜欢
  • 2016-10-30
  • 2011-05-21
  • 2021-08-06
  • 2011-10-18
  • 1970-01-01
  • 2020-06-17
  • 2021-05-02
  • 2021-05-28
  • 1970-01-01
相关资源
最近更新 更多