【问题标题】:C# compare fields from different lines in csvC#比较csv中不同行的字段
【发布时间】:2016-03-31 20:19:06
【问题描述】:

我正在尝试比较一行中数组的 0 索引中的值和下一行中的 0 索引中的值。想象一个 CSV,其中我在第一列中有一个唯一标识符,在第二列中有一个对应的值。

USER1, 1P
USER1, 3G
USER2, 1P
USER3, 1V

我想检查 [0] 的值下一行(或上一行,如果这样更容易)进行比较,如果它们相同(如示例中所示)将其连接到索引 1。也就是说,数据应该读作

USER1, 1P, 3G
USER2, 1P
USER3, 1V

在它被传递到下一个函数之前。到目前为止我有

 private void csvParse(string path)
        {
            using (TextFieldParser parser = new TextFieldParser(path))
                {
                    parser.Delimiters = new string[] { "," };
                    while (!parser.EndOfData)
                    {
                        string[] parts = parser.ReadFields();
                        if (parts == null)
                        {
                            break;
                        }
                        contact.ContactId = parts[0];
                        long nextLine;
                        nextLine = parser.LineNumber+1;
//if line1 parts[0] == line2 parts[0] etc.
                    }
                }
            }

有人有什么建议吗?谢谢你。

【问题讨论】:

  • 如果结尾有一行USER1, 1X,输出会是什么?
  • @Eser 生成的报告是以所有相同的唯一标识符都连续写入的方式编写的。也就是说,如果 USER1 出现,它将全部出现在一个区域中,而不是在整个文档中随机出现。

标签: c# csv textfieldparser


【解决方案1】:

如何将数组保存到变量中:

private void csvParse(string path)
        {
            using (TextFieldParser parser = new TextFieldParser(path))
                {
                    parser.Delimiters = new string[] { "," };
                    string[] oldParts = new string[] { string.Empty };
                    while (!parser.EndOfData)
                    {
                        string[] parts = parser.ReadFields();
                        if (parts == null || parts.Length < 1)
                        {
                            break;
                        }

                        if (oldParts[0] == parts[0])
                        {
                             // concat logic goes here
                        }
                        else
                        {
                            contact.ContactId = parts[0];
                        }

                        long nextLine;
                        nextLine = parser.LineNumber+1;
                        oldParts = parts;
//if line1 parts[0] == line2 parts[0] etc.
                    }
                }
            }

【讨论】:

  • 此解决方案有效,实施所需的工作量最少。 parts[0] 与 oldParts[0] 相比,如果它们相同,则将 parts[1] 连接到 oldParts[1] 以发送到下一个函数。
【解决方案2】:

如果我对您的理解正确,您要问的本质上是“如何根据第一列中的值对第二列中的值进行分组?”。

一个快速而简洁的方法是Group By using LINQ

var linesGroupedByUser =
    from line in File.ReadAllLines(path)
    let elements = line.Split(',')
    let user = new {Name = elements[0], Value = elements[1]}
    group  user by user.Name into users
    select users;

foreach (var user in linesGroupedByUser)
{
    string valuesAsString = String.Join(",", user.Select(x => x.Value));

    Console.WriteLine(user.Key + ", " + valuesAsString);
}

我没有使用您的 TextFieldParser 类,但您可以轻松地使用它。但是,这种方法确实要求您有能力将所有数据加载到内存中。你没有提到这是否可行。

【讨论】:

    【解决方案3】:

    执行此类操作的最简单方法是将每一行转换为一个对象。您可以使用CsvHelperhttps://www.nuget.org/packages/CsvHelper/ 为您完成工作,或者您可以迭代每一行并解析为一个对象。这是一个很棒的工具,它知道如何正确地将 CSV 文件解析为对象集合。然后,无论您是自己创建集合还是使用CsvHelper,您都可以使用LinqGroupByhttps://msdn.microsoft.com/en-us/library/bb534304(v=vs.100).aspx、您的“密钥”(在本例中为UserId)和Aggregatehttps://msdn.microsoft.com/en-us/library/bb549218(v=vs.110).aspx、其他属性转换为字符串。然后,您可以将新的、分组依据的集合用于您的最终目标(将其写入文件或将其用于您需要的任何内容)。

    【讨论】:

      【解决方案4】:

      您基本上是在找到所有唯一条目,因此将它们放入字典中,并以联系人 ID 作为键。如下:

       private void csvParse(string path)
          {
              using (TextFieldParser parser = new TextFieldParser(path))
              {
                  parser.Delimiters = new string[] { "," };
                  Dictionary<string, List<string>> uniqueContacts = new Dictionary<string, List<string>>();
                  while (!parser.EndOfData)
                  {
                      string[] parts = parser.ReadFields();
                      if (parts == null || parts.Count() != 2)
                      {
                          break;
                      }
                      //if contact id not present in dictionary add
                      if (!uniqueContacts.ContainsKey(parts[0]))
                          uniqueContacts.Add(parts[0],new List<string>());
                      //now there's definitely an existing contact in dic (the one 
                      //we've just added or a previously added one) so add to the                   
                      //list of strings for that contact
                      uniqueContacts[parts[0]].Add(parts[1]);
                  }
      
                  //now do something with that dictionary of unique user names and
                  // lists of strings, for example dump them to console in the 
                  //format you specify:
      
                  foreach (var contactId in uniqueContacts.Keys)
                  {
      
                      var sb = new StringBuilder();
                      sb.Append($"contactId, ");
                      foreach (var bit in uniqueContacts[contactId])
                      {
                          sb.Append(bit);
                          if (bit != uniqueContacts[contactId].Last())
                              sb.Append(", ");
                      }
                      Console.WriteLine(sb);
                  }
              }
          }
      

      【讨论】:

      • 与其简单地标记,请告诉我这个想法有什么问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多