CSV со встроенными запятыми с использованием C # .Net 4.0 LINQ - PullRequest
2 голосов
/ 02 декабря 2011

Я пытаюсь найти элегантный способ чтения строки cvs через 4.0 linq, и мне это не помогло из-за запятых между кавычками.Вот пример из 3 столбцов и 3 строк:

Дата, Годы, MemoText "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?

Ответы [ 4 ]

3 голосов
/ 02 декабря 2011

Фактический ответ для .Net предлагает не делать это самостоятельно - существует множество сторонних библиотек, которые сделают это просто:

Импорт файлов CSV в .Net

2 голосов
/ 15 декабря 2012

Вот отличный метод расширения Эрика Уайта, который удовлетворит ваши требования CSV:

  • Для символа-разделителя допустима только запятая
  • Значения могут быть указаны. Цитаты обрезаны
  • Кавычки могут иметь внутренние запятые
  • Значения в кавычках также могут иметь внутренние escape-последовательности: обратный слеш сопровождаемый любым символом, включая кавычку (\ ”), обратную косую черту (\) или любой другой символ (\ a)
  • CsvSplit выдаст исключение для неправильно отформатированных строк

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

0 голосов
/ 03 декабря 2011

Вот код, который я использовал на тот случай, если он пригодится кому-то в будущем или кто-то еще почувствует желание его настроить.

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
                }
            }
        }
    }
}
0 голосов
/ 02 декабря 2011

Если вы можете изменить структуру файла, я бы порекомендовал вам найти уникальный разделитель, который не используется где-либо еще в вашем контенте (например, ';').

Тогда использование сторонней библиотеки (например, LINQ to CSV ) явно облегчит задачу.

Вы можете использовать чистый синтаксис, подобный этому:

var memos = from p in myFile
            select new { p.Date, p.DecimalYears, p.MemoText };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...