首页 > 解决方案 > 如何将具有不同列标题的 .csv 文本文件中的数据导入内存

问题描述

我正在尝试一次从 CSV 文件中读取一行数据。第一行,列标题,确定每行中的数据类型。我已经知道所有数据都属于我的项目中名为 UserModel 的类,但我试图做到这一点,而不将数据硬编码到 UserModel 字段中。我能够阅读前几行,并正确创建用户模型的第一个实例。但是当我尝试阅读第三行时,事情就崩溃了。我正在使用字典,并且在阅读前几行时尝试使用创建的字典的实例。意思是在 Dictionary 实例中使用键的顺序,并用下一组数据更新值,但我不明白为什么这组数据不与正确的键对齐。

我在这里得到了一些很棒的代码并使用了它。DictionaryToObject() 方法就是其中之一。我认为我使用的循环比我需要的要多,并且不知道如何使用其他方法来访问我拥有的某些数据结构,如字符串数组、列表和字典。我是新手,可能使用了错误的数据结构,甚至是一些不必要的代码来尝试获得我想要的结果。

public class MyData 
{
    BindingList<UserModel> users = new BindingList<UserModel>();
    UserModel currentUser = new UserModel();

    public BindingList<UserModel> GetUsers()
    {

        string[] lines = File.ReadAllLines("StandardDataSet.csv");
        // Add error handing to make sure all lines have four enteris 
        foreach (string line in lines.Skip(1))
        {
            //I am not sure how to handle the first line that is a header
            string [] values = line.Split(',').Select(x => x.Trim()).ToArray();
            currentUser.FirstName = values[0];
            currentUser.LastName = values[1];
            int.TryParse(values[2], out int age);
            currentUser.Age = age;
            bool.TryParse(values[3], out bool alive);
            currentUser.IsAlive = alive;
            users.Add(currentUser);
        }
        return users;
    }
private static T DictionaryToObject<T>(IDictionary<string, object> dict) where T : new()
    {
        T t = new T();
        PropertyInfo[] properties = t.GetType().GetProperties();

        foreach (PropertyInfo property in properties)
        {
            if (!dict.Any(x => x.Key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase)))
                continue;
            KeyValuePair<string, object> item = dict.First(x => x.Key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase));
            Type tPropertyType = t.GetType().GetProperty(property.Name).PropertyType;
            Type newT = Nullable.GetUnderlyingType(tPropertyType) ?? tPropertyType;
            //if (item.Value is '0') Convert.ToBoolean(Convert.ToInt32(item.Value));
            object newA = Convert.ChangeType(item.Value, newT);
            t.GetType().GetProperty(property.Name).SetValue(t, newA, null);
        }
        return t;
    }
public BindingList<UserModel> GetUsersAdvanced()
    {
        users = new BindingList<UserModel>();
        var dict= new Dictionary<string,object>();
        string[] lines = File.ReadAllLines("AdvancedDataSet.csv");
        var header = lines.First().Split(',');
        var data = lines.Skip(1).First().Split(',');


        for (int i = 0; i < header.Length; i++)
            {
                if (data[i] == "0" || data[i] == "1")
                    {
                        Boolean IsAlive = Extensions.ToBoolean(data[i]);
                        dict.Add(header[i], IsAlive);
                    }
                else
                //if (data[i]!= "0" || data[i]!="1")
                dict.Add(header[i], data[i]);
        }   
        currentUser = DictionaryToObject<UserModel>(dict);
        var dictCopy = new Dictionary<string, object>(dict);
        var dictConcurrent = new ConcurrentDictionary<string, object>(dict);

    foreach (var line in lines.Skip(1))
    {
    if (line != null)  data = line.Split(',');

                foreach (var key in dict.Keys)
                {
                int i = 0;
                if (data[i] == "0" || data[i] == "1")
                    {
                        Boolean IsAlive = Extensions.ToBoolean(data[i]);
                        dictCopy[key] = IsAlive;
                        i++;
                        continue;
                    }
                    else
                    dictCopy[key] = data[i];
                    i++;  
                 }
                 currentUser = DictionaryToObject<UserModel>(dictCopy);
                 users.Add(currentUser);
                 currentUser = new UserModel();

    }
 return users;
}

标签: c#

解决方案


正如人们的评论所说,我还建议使用现有的库。它们通常还支持您在代码中执行的到对象的数据映射数据转换。所以,先试试一两个。

怎么样:

顺便说一句,就 Dictionary 中的项目顺序而言,您可以在此处阅读原因

出于枚举的目的,字典中的每个项目都被视为表示值及其键的 KeyValuePair 结构。返回项目的顺序未定义。

那么,如何将列标题保留在列表中呢?这样,您可以保持订单。代码如下所示。

但请仅以订购商品为例。同样,首先尝试一些库,然后您会看到更酷的功能并获得更整洁的代码。

public BindingList<UserModel> GetUsersAdvanced()
{
    users = new BindingList<UserModel>();
    string[] lines = File.ReadAllLines("AdvancedDataSet.csv");
    var cols = lines.First().Split(',').ToList();

    // validate column name duplication here!

    foreach (var line in lines.Skip(1))
    {
        if (line != null)  data = line.Split(',');

        for (var i = 0; i < cols.Count(); i++)
        {
            var key = cols[i];
            var dict = new Dictionary<string, object>();

            if (data[i] == "0" || data[i] == "1")
            {
                Boolean IsAlive = Extensions.ToBoolean(data[i]);
                dict.Add(key, IsAlive);
            }
            else
                dict.Add(key, data[i]);

            var currentUser = DictionaryToObject<UserModel>(dict);
            users.Add(currentUser);
        }

        return users;
    }
}

推荐阅读