C# 读取数据 (CSV) 和搜索

C# Read from Data (CSV) & Search

我对从 CSV 获取数据有疑问,或者是否有推荐的其他格式?我应该使用什么数组、字典或查找?

在我的 WPF 对象中,我计划有一个搜索框和一个显示搜索结果的列表框。

数据库由两列组成,例如

Shelly, Good
Joe, Happy
Albert, Clever
Shelly, Pretty
Jessica, Lovely
Joe, Great

所以第一列是某人的名字,第二列是它的形容词。我只是举个例子来说明。基本上我会搜索第一列,一个名字可能有 "n" 个形容词,甚至只有 1 个。

我可以使用字典数据格式吗?但如果是这样的话,如果我在浏览这些行时遇到另一个值,我可以添加到键 "Shelly" 上吗?

还是使用 Lookup 更好?

CSV 文件的大小可能约为 30000 行,2 列。当我搜索 Shelly 时,我希望得到 Good, Pretty,我可以用它来填充我的列表框。

谢谢!

我会使用 Dictionnary <string, List<string>> 来存储值。

在您的情况下,名称似乎是键并且可能有多个值。

当您解析 CSV 时,您需要检查字典是否包含关键字 "Shelly" 如果不包含,则创建条目 <"Shelly, new list{"您实际得到的形容词">

如果键 "Shelly" 存在,您只需将当前形容词添加到值列表中。

可以使用;

IDictionary<string, IList<string>> myData = new Dictionary<string, IList<string>>();

Key value 包含 Shelly,List 包含 {Good, Pretty}

使用以下存储值

Dictionary<string, List<string>> databaseData = new  Dictionary<string, List<string>>();

并在下方搜索使用:

List<string> results; 
databaseData.TryGetValue("Shelly", out results);

Shelly 替换为您的搜索变量。对于上面的示例,结果将保持 Good, Pretty.

不会为此使用嵌套的collections。你确定你会记得下周所有这些字符串的作用吗?获得形容词之一所需的双重索引怎么样,例如 dict["Lucy"][1] ?

创建一个小 class,其中包含名称和该名称的形容词,并绑定到其实例列表。从 CSV 创建列表并不比创建嵌套结构更难(实际上 更容易 )。您可以将名称用作字典的键,以便于查找。

您可以使用接受关键字和内容选择器的 GroupBy overload 将名称与形容词分开。

例如:

class Person
{
    public string Name{get;set;}
    public string[] Adjectives{get;set;}
}

Dictionary<string,Persons> _persons;

var lucysAdjectives = _persons["Lucy"].Adjectives;

要创建列表,您可以像创建字典一样对 CSV 值进行分组。假设您的 CSV 解析器 return 是 Item1、Item2 objects 的序列,您可以编写:

_persons = csvRows.GroupBy(
                            pair=>pair.Item1, //The key to group by
                            pair=>pair.Item2  //The group's contents
                      )
                     .ToDictionary(
                            g=>g.Key,
                            new Person{
                                        Name=g.Key,
                                        Adjectives=g.ToArray()
                            }
                      );

假设文件不包含 headers、引号等,您可以只读取每一行并将其拆分。在这种情况下,您可以像这样将文件读入 _persons 字典:

_persons = File.ReadLines(pathToFile)
               .Select( line  => line.Split(','))
               .GroupBy( pair => pair[0], 
                         pair => pair[1])
               .ToDictionary( g => g.Key,
                              new Person{ Name=g.Key, Adjectives=g.ToArray()});

您可以通过向接受名称的 Person 添加一个构造函数来简化代码,并且 IEnumerable<string> and/or 使其不可变以防止意外修改,例如:

class Person
{
    public string Name{get;}
    public string[] Adjectives{get;}

    Person(string Name,IEnumerable<string> adjectives)
    {
        Name=name;
        Adjectives=adjectives.ToArray();
    }
}


_persons = File.ReadLines(pathToFile)
               .Select( line  => line.Split(','))
               .GroupBy( pair => pair[0], 
                         pair => pair[1])
               .ToDictionary( g => g.Key,
                              new Person(g.Key, g));

或者从组中创建人 objects,然后从人中创建字典

_persons = File.ReadLines(pathToFile)
               .Select( line  => line.Split(','))
               .GroupBy( pair => pair[0], 
                         pair => pair[1])
               .Select( g => new Person(g.Key, g)) 
               .ToDictionary( person => person.Name,
                              person => person);

更新

如果您不想创建小 class(为什么?)并且字典的范围很小,例如单个方法,您可以使用 C# 7 的元组。

var persons = File.ReadLines(pathToFile)
               .Select( line  => line.Split(','))
               .GroupBy( pair => pair[0], 
                         pair => pair[1])
               .Select( g => (Name=g.Key, Adjectives=g.ToArray())) 
               .ToDictionary( person => person.Name,
                              person => person);

范围必须很小,因为元组名称只是编译器的魔法。这些字段实际上被命名为 Item1、Item2 等。编译器魔术允许您按名称引用它们。

您也不能定义元组类型或别名,这意味着您必须在 return 类型、字段声明等中重复元组定义:

Dictionary<string,(string Name,string[] Adjectives)> _persons ;

代码很快变得丑陋