【问题标题】:CSV with embedded commas using C# .Net 4.0 LINQ使用 C# .Net 4.0 LINQ 嵌入逗号的 CSV
【发布时间】:2012-01-11 11:26:26
【问题描述】:

我正在尝试找到一种通过 4.0 linq 读取 cvs 字符串的优雅方法,但由于引号之间嵌入了逗号而有些不成功。下面是一个 3 列 3 行的例子:

日期、年份、备忘录文本 "2011-01-01","0.5","备忘录文本
备忘录文本继续
还在继续,然后是逗号,但备忘录是用引号引起来的"
"2010-01-01","0.5","备忘录文本,没有换行符的备忘录"
"2009-01-01","1.0","纯备忘录文本"

到目前为止,我已经提出了以下错误代码作为将其他堆栈交换位组合在一起。这不起作用,因为备忘录文本中的回车换行符将备忘录文本分成多个字段。

using (var reader = new StreamReader(getReader))
{
    var records = reader.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
    var enumRecords = records.Skip(1).Take(1);
    using (var dc = new DataContext())
    {
        foreach (var record in enumRecords
            .Select(x => x.Trim()
            .Split(new char[] { ',' }))
            .Select(fields => new Entity
            {
                Date = (!string.IsNullOrEmpty(record.ElementAt(0))) ? Convert.ToDateTime(record.ElementAt(0)) : default(DateTime),
                DecimalYears = record.ElementAt(1),
                MemoText = record.ElementAt(2)
            }))
        {
            //Commit DataContext
        }
    }
}

单独使用逗号分割时没有骰子,因为引用的文本之间存在逗号:

using (var reader = new StreamReader(getReader))
{
    var sdata = reader.ReadToEnd();

    using (var dc = new DataContext())
    {
        var query = sdata
            .Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)
            .Replace(Environment.NewLine, string.Empty)
            .Replace("\"\"", "\",\"")
            .Select((i, n) => new { i, n })
            .GroupBy(a => a.n / 3)
            .Skip(1).Take(1);

        foreach (var fields in query)
        {
            var newEntity = new Entity();
            newEntity.Date = (!string.IsNullOrEmpty(fields.ElementAt(0).i)) ? Convert.ToDateTime(fields.ElementAt(0).i) : default(DateTime);
            newEntity.DecimalYears = fields.ElementAt(1).i;
            newEntity.MemoText = fields.ElementAt(2).i;
        }
    }
}

到目前为止,似乎一个简单的目标是接近冗长丑陋的代码,可能有人有一个干净而实用的方法来使用 LINQ 来解决这个问题?

【问题讨论】:

    标签: linq c#-4.0 .net-4.0 csv


    【解决方案1】:

    .Net 的实际答案建议不要自己动手 - 有大量第三方库可以让这变得简单:

    CSV File Imports in .Net

    【讨论】:

    • 我想我已经有点决心在 LINQ .net 4.0 中完成这项工作,我是否可能在上述代码 sn-ps 中仅差一行或多行?
    • 当然,可能还有很多我没有遇到过的情况,这只是我目前所知道的。
    • 文件解析是一个在很多项目中都被打破的话题。一切都很好,除非有人在某个字段中输入了错误的值,或者您雇用了 O'Connor 先生等等。我只是利用那些在我之前经历过所有痛苦的人的教训:-)
    • 我最终使用了您附加的 stackoverflow 链接中的 TextFieldParser。效果很好,非常感谢!
    【解决方案2】:

    这是 Eric White 的一个很好的扩展方法,可以处理您的 CSV 要求:

    • 只有逗号对分隔符有效
    • 可以引用值。引号被修剪
    • 引用的值可以有内部逗号
    • 引用的值也可以有内部转义序列:反斜杠 后跟任何字符,包括引号 (\")、反斜杠 (\) 或 任何其他字符 (\a)
    • CsvSplit 会对格式不正确的字符串抛出异常

    http://blogs.msdn.com/b/ericwhite/archive/2008/09/30/linq-to-text-and-linq-to-csv.aspx

    【讨论】:

      【解决方案3】:

      如果您可以更改文件的结构,我建议您找到一个不会在您的内容的其他地方使用的唯一分隔符(即“;”)。

      然后,使用第三方库(如LINQ to CSV)显然会减轻任务。

      您可以使用这样的简洁语法:

      var memos = from p in myFile
                  select new { p.Date, p.DecimalYears, p.MemoText };
      

      【讨论】:

      • 哇,是的,如果我能够控制渲染 csv,那就太好了。不幸的是,它来自salesforce bulkapi。我可以重新格式化输出,但可能会产生额外的开销。
      • 至少你不是唯一一个遭受这个问题的人:success.salesforce.com/ideaview?id=08730000000BpAeAAK
      【解决方案4】:

      这是我使用的代码,以防将来对某人有帮助,或者其他人有微调它的冲动。

      using (var reader = new StreamReader(Service.GetResult(batchInfo, results.result[0])))
      {
          using (var dc = new DataContext())
          {
              using (var parser = new TextFieldParser(reader))
              {
                  parser.Delimiters = new string[] { "," };
                  parser.TrimWhiteSpace = true;
                  while (true)
                  {
                      string[] parts = parser.ReadFields();
                      if (parts == null) { break; }
                      try
                      {
                          var newEntity = new Entity();
                          newEntity.ID = Guid.NewGuid();
                          newEntity.Date = (!string.IsNullOrEmpty(parts[0])) ? Convert.ToDateTime(parts[0]) : default(DateTime);
                          newEntity.Year = parts[1];
                          newEntity.MemoText = parts[2];
                          dc.Entity.InsertOnSubmit(newEntity);
                          dc.SubmitChanges();
                      }
                      catch (MalformedLineException mle)
                      {
                          string message = mle.Message;
                          //TODO: log an error
                      }
                  }
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-07
        • 2019-03-28
        • 1970-01-01
        • 1970-01-01
        • 2023-03-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多