【问题标题】:Convert C# DataTable(ColumnName, ColumnType, ColumnValue) to csv and restore back将 C# DataTable(ColumnName, ColumnType, ColumnValue) 转换为 csv 并恢复
【发布时间】:2015-10-14 11:55:40
【问题描述】:

我正在寻找将 ADO.NET 数据表转换为 csv 文件的 c# 代码,但是我想保存/恢复

  • 列名,
  • 列数据类型和
  • 列值

在 csv 中。我发现的大多数解决方案都以字符串列类型从 CSV 恢复数据表。我还希望可以将可为空的值恢复为 DBNull.Value。 DateTime 列应仅保存和恢复为 DateTime 类型。这个概念是使用 Oracle/Sqlserver 数据库中的 DataAdapter 填充数据表,然后将该表保存到 CSV 文件,然后从 CSV 恢复。

我使用下面链接中的代码使用 DataTableExtensions 类 c# datatable to csv 将 DataTable 保存到 CSV 文件

为了将 CSV 文件读回 DataTable,我使用了以下链接 http://www.codeproject.com/Articles/11698/A-Portable-and-Efficient-Generic-Parser-for-Flat-F

问题是当我将 CSV 文件恢复到数据表时,我必须从数据表行创建实体。但是他们在 InvalidCast 上抛出异常。

【问题讨论】:

    标签: c# datatable ado.net export-to-csv


    【解决方案1】:

    假设您想将列名存储在第一行,类型存储在第二行,数据从第三行开始,您可以使用以下代码。样本数据:

    DataTable tblExport = new DataTable();
    tblExport.Columns.Add("ID", typeof(int));
    tblExport.Columns.Add("Name", typeof(string));
    tblExport.Columns.Add("DateofBirth", typeof(DateTime)).AllowDBNull = false;
    tblExport.Columns.Add("DateofDeath", typeof(DateTime)).AllowDBNull = true;
    
    tblExport.Rows.Add(1, "Tim", new DateTime(1973, 7, 9), DBNull.Value);
    tblExport.Rows.Add(2, "Jim", new DateTime(1953, 3, 19), new DateTime(2011, 1, 2));
    tblExport.Rows.Add(3, "Toby", new DateTime(1983, 4, 23), DBNull.Value);
    

    由于您需要使用 value.ToString 将所有值转换为字符串,因此我将在开始时将区域性更改为 InvariantCulture 以强制使用特定的 DateTime 格式,请存储旧的,以便您可以在最后再次启用它。我希望代码是不言自明的:

    var oldCulture = CultureInfo.CurrentCulture;
    System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
    

    将 DataTable 写入 csv 文件

    string delimiter = "\t"; // tab separated
    StringBuilder sb = new StringBuilder();
    // first line column-names
    IEnumerable<string> columnNames = tblExport.Columns.Cast<DataColumn>()
        .Select(column => column.ColumnName);
    sb.AppendLine(string.Join(delimiter, columnNames));
    // second line column-types
    IEnumerable<string> columnTypes = tblExport.Columns.Cast<DataColumn>()
        .Select(column => column.DataType.ToString());
    sb.AppendLine(string.Join(delimiter, columnTypes));
    // rest: table data
    foreach (DataRow row in tblExport.Rows)
    {
        IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
        sb.AppendLine(string.Join(delimiter, fields)); 
    }
    string path = @"C:\Temp\Testfile.csv";
    File.WriteAllText(path, sb.ToString());
    

    将 csv 文件读入 DataTable

    string[] lines = File.ReadAllLines(path);
    string[] columns = lines[0].Split(new[] { delimiter }, StringSplitOptions.None);
    string[] types   = lines[1].Split(new[] { delimiter }, StringSplitOptions.None);
    DataTable tblImport = new DataTable();
    for (int i = 0; i < columns.Length; i++)
    {
        string colName  = columns[i];
        string typeName = types[i];
        tblImport.Columns.Add(colName, Type.GetType(typeName));
    }
    
    // import data
    // use a typeValueConverter dictionary to convert values:
    var typeValueConverter = new Dictionary<Type, Func<string, object>> {
        { typeof(DateTime), value =>  value.TryGetDateTime(null, null) },
        { typeof(Decimal),  value =>  value.TryGetDecimal(null) },
        { typeof(int),      value =>  value.TryGetInt32(null) },
    };
    foreach (string line in lines.Skip(2))
    { 
        string[] fields = line.Split(new[]{ delimiter }, StringSplitOptions.None);
        DataRow r = tblImport.Rows.Add(); // already added at this point
        for (int i = 0; i < tblImport.Columns.Count; i++)
        {
            DataColumn col = tblImport.Columns[i];
            string rawValue = fields[i];
            object val = rawValue;
            if (typeValueConverter.ContainsKey(col.DataType))
                val = typeValueConverter[col.DataType](rawValue);
            else if (col.DataType != typeof(string) && string.IsNullOrEmpty(rawValue))
                val = DBNull.Value;
            r.SetField(col, val);
        }
    }
    System.Threading.Thread.CurrentThread.CurrentCulture = oldCulture;
    

    当然你应该在两种方法中将两者分开,一种用于导出,一种用于导入。

    我使用了我的扩展方法TryGetDateTimeTryGetDecimalTryGetInt32,它们将字符串解析为DateTime?Decimal?int?(如果无法解析则为null)。它们在 LINQ 查询中特别方便:

    public static DateTime? TryGetDateTime(this string item, DateTimeFormatInfo dfi, params string[] allowedFormats)
    {
        if (dfi == null) dfi = DateTimeFormatInfo.InvariantInfo;
        DateTime dt;
        bool success;
        if(allowedFormats == null)
            success = DateTime.TryParse(item, dfi, DateTimeStyles.None, out dt);
        else
            success = DateTime.TryParseExact(item, allowedFormats, dfi, DateTimeStyles.None, out dt);
        if (success) return dt;
        return null;
    }
    
    public static decimal? TryGetDecimal(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
    {
        if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
        decimal d = 0m;
        bool success = decimal.TryParse(item, nStyles, formatProvider, out d);
        if (success)
            return d;
        else
            return null;
    }
    
    public static int? TryGetInt32(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
    {
        if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
        int i = 0;
        bool success = int.TryParse(item, nStyles, formatProvider, out i);
        if (success)
            return i;
        else
            return null;
    }
    

    【讨论】:

    • 嗨蒂姆,感谢您的代码,但在导入数据时失败。出现错误无法将 存储在 SALARY 列中。预期类型为十进制。由于桌子太大,我仍在检查可能是什么原因
    • @Hemendr:你使用制表符作为分隔符还是逗号?
    • 由于数据也有逗号和制表符,所以我使用自定义分隔符 = "|*|"
    • 在将数据类型存储在第二行时,我们可以将 System.Decimal 存储为可为空的十进制吗?所有列类型都应该可以为空。在您的代码中,它在这一行失败 r.SetField(col, val) where col.DataType is Deciaml 但 val datatype is string with value ""
    • 太棒了!!。感谢您的大力帮助蒂姆!它现在工作
    猜你喜欢
    • 1970-01-01
    • 2010-10-27
    • 2020-09-04
    • 2011-11-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 2013-11-11
    相关资源
    最近更新 更多