【问题标题】:Convert IEnumerable string array to datatable将 IEnumerable 字符串数组转换为数据表
【发布时间】:2014-11-13 09:40:42
【问题描述】:

我有一个用竖线(|)分隔的 csv 文件。我正在使用以下代码行阅读它:

IEnumerable<string[]> lineFields = File.ReadAllLines(FilePath).Select(line => line.Split('|'));

现在,我需要将其绑定到 GridView。所以我正在创建一个动态DataTable,如下所示:

DataTable dt = new DataTable();
int i = 0;
foreach (string[] order in lineFields)
{
    if (i == 0)
    {                        
        foreach (string column in order)
        {
            DataColumn _Column = new DataColumn();
            _Column.ColumnName = column;
            dt.Columns.Add(_Column);
            i++;
            //Response.Write(column);
            //Response.Write("\t");
        }
    }
    else
    {
        int j = 0;
        DataRow row = dt.NewRow();
        foreach (string value in order)
        {
            row[j] = value;                            
            j++;

            //Response.Write(column);
            //Response.Write("\t");
        }
        dt.Rows.Add(row);
    }
    //Response.Write("\n");
}

这很好用。但我想知道是否有更好的方法将IEnumerable&lt;string[]&gt; 转换为DataTable。我需要阅读很多这样的 CSV,所以我认为上面的代码可能存在性能问题。

【问题讨论】:

  • 如果它用| 分隔,那么它不是CSV。它们将是逗号分隔值。
  • @Jodrell 也可以参考字符分隔值
  • 有一些自动生成软件可以创建这些文件。
  • @DarrenYoung,你有什么参考资料来支持它,它对我来说是新的,通过快速搜索不是很明显?
  • 你能假设每一行的列数都和第一行一样吗?

标签: c# asp.net .net linqtocsv


【解决方案1】:

从 .Net 4 开始:

使用ReadLines

DataTable FileToDataTable(string FilePath)
{

    var dt = new DataTable();

    IEnumerable<string[]> lineFields = File.ReadLines(FilePath).Select(line => line.Split('|'));
    dt.Columns.AddRange(lineFields.First().Select(i => new DataColumn(i)).ToArray());

    foreach (var order in lineFields.Skip(1))
        dt.Rows.Add(order);

    return dt;
}

edit: 代替此代码,使用@Jodrell 答案的代码,这样可以防止枚举器的双重充电)。

.Net 4 之前:

使用流式传输:

DataTable FileToDataTable1(string FilePath)
{
    var dt = new DataTable();

    using (var st = new StreamReader(FilePath))
    {
        // first line procces
        if (st.Peek() >= 0)
        {
            var order = st.ReadLine().Split('|');
            dt.Columns.AddRange(order.Select(i => new DataColumn(i)).ToArray());
        }

        while (st.Peek() >= 0)
            dt.Rows.Add(st.ReadLine().Split('|'));
    }
    return dt;
}

【讨论】:

  • 如果假设每一行的列数相同,则应删除循环中的条件,而是在循环开始读取第一行并检查列数之前添加代码仅第一行。
  • 如果我删除条件,它工作正常。但标题列也被创建为顶部的一行。
  • 文件的第一行有列标题。
  • 这不会添加列标题。
  • 如果您使用ReadLines @Jodrell 代码,它的效率会稍微高一些(它只创建一个枚举器实例)。
【解决方案2】:

因为在您的链接示例中,该文件有一个标题行。

const char Delimiter = '|';

var dt = new DataTable;
using (var m = File.ReadLines(filePath).GetEnumerator())
{
    m.MoveNext();
    foreach (var name in m.Current.Split(Delimiter))
    {
        dt.Columns.Add(name);
    }

    while (m.MoveNext())
    {
        dt.Rows.Add(m.Current.Split(Delimiter));
    }
}

这会一次性读取文件

【讨论】:

  • @jordell:这就是我要找的。​​span>
  • @jodrell 我的代码loading it all into memory?据我所知,他甚至一次都没有。见referencesource.microsoft.com/#System.Core/System/Linq/…
  • @lomed,由于ReadLines/ReadLine,您的操作仅将其加载到DataTable,正如您所注意到的,OP 使用ReadAllLines。如果进行基准测试,ReadAllLines 对于较小的文件可能会稍微快一些。但是,对于大文件,内存的有效使用可能是一个更大的问题。
  • @jordell 你说得对。但是如果你使用 ReadAllLines,你可以使用普通的For。不过,您需要修改您的文字,通过两次加载文件提出了一个建议。
猜你喜欢
  • 2018-06-16
  • 1970-01-01
  • 2014-07-03
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
相关资源
最近更新 更多