Разберите CSV, где заголовки содержат пробелы, с помощью CsvHelper

У меня есть файл CSV с заголовками полей, и некоторые из них содержат два или три слова, разделенных пробелами:

Снимок экрана первых нескольких строк электронной таблицы в приложении для работы с электронными таблицами, включая заголовки, содержащие пробелы, как описано выше.

На картинке выше вы можете видеть заголовки полей, содержащие пробелы:
«Время дня», «Имя процесса» и «Путь к изображению».

Когда я попытался прочитать CSV, вызвав reader.GetRecords<DataRecord>(); (где DataRecord - класс, который я определил), я получил сообщение об ошибке:

Поля "TimeOfDay" не существуют в файле CSV. "*

Это потому, что мой класс DataRecord не может содержать членов с пробелами.

Как я могу использовать CsvHelper для анализа файла CSV?


person E235    schedule 08.11.2014    source источник


Ответы (2)


На основе документации CsvHelper существует несколько способов достижения желаемых результатов.

1. Игнорировать пробелы в заголовках (что, по моему мнению, должно легко решить вашу проблему)

В CsvHelper 3 или новее используйте PrepareHeaderForMatch (задокументировано на http://joshclose.github.io/CsvHelper/configuration#headers), чтобы удалить пробелы из заголовков:

csv.Configuration.PrepareHeaderForMatch =
    header => Regex.Replace(header, @"\s", string.Empty)

В CsvHelper 2 установите флаг IgnoreHeaderWhiteSpace, который указывает читателю игнорировать пробелы в заголовках при сопоставлении столбцов со свойствами по имени.

reader.Configuration.IgnoreHeaderWhiteSpace = true;

2. Читать вручную

Мы можем прочитать каждое поле вручную, например:

var reader = new CsvReader(sr);
do
{
    reader.Read();                   
    var record=new DataRecord();

    record.TimeOfDay=reader.GetField<string>("Time of Day");
    record.ProcessName=reader.GetField<string>("Process Name");
    record.PID=reader.GetField<string>("PID");
    record.Operation=reader.GetField<string>("Operation");
    record.Path=reader.GetField<string>("Path");
    record.Result=reader.GetField<string>("Result");
    record.Detail=reader.GetField<string>("Detail");
    record.ImagePath=reader.GetField<string>("Image Path");

} while (!reader.IsRecordEmpty());

3. Сопоставление классов:

Мы можем вручную сопоставить свойства нашего класса и заголовки в файле CSV, используя name class mapping следующим образом:

public sealed class DataRecordMap:CsvClassMap<DataRecord>
{
    public DataRecordMap()
    {
         Map( m => m.TimeOfDay).Name("Time Of Day");
         Map( m => m.ProcessName).Name("Process Name");
         Map( m => m.PID).Name("PID");   
         Map( m => m.Operation).Name("Operation");    
         Map( m => m.Path).Name("Path");
         Map( m => m.Result).Name("Result");
         Map( m => m.Detail).Name("Detail");
         Map( m => m.ImagePath).Name("Image Path");
     }
}

Затем мы должны зарегистрировать его, используя:

reader.Configuration.RegisterClassMap<DataRecordMap>();
person user3473830    schedule 08.11.2014
comment
Хотя это выглядит хорошо, похоже, что метод 1 не работает (больше). Ожидаемая функция теперь принимает два параметра. Однако после его добавления кажется, что возвращенные значения не используются, метод не имеет никакого эффекта. - person kjyv; 08.03.2019
comment
@kjyv У меня работает с csv.Configuration.PrepareHeaderForMatch = (header, index) => Regex.Replace(header, @"\s", string.Empty); - person fuzzy_logic; 24.03.2019
comment
Просто любопытно, написал ли кто-нибудь отладчик несоответствия имени столбца, чтобы вместо того, чтобы выдавать ошибку, говоря, что он не может найти совпадение, он пытался найти любые столбцы, соответствующие любому из слов на карте. Например, у меня есть столбец с именем «Рейтинговое агентство», и я получил сообщение об ошибке «Ничего не существует». Хотя я хочу, чтобы он сильно потерпел неудачу в разработке, мне также понравилось бы, если бы в разработке говорилось, что следующие столбцы частично совпадают: Рейтинговые агентства, Рейтинги - person John Zabroski; 17.05.2019

Библиотека теперь поддерживает атрибуты. Вы можете использовать атрибут Name.

using CsvHelper.Configuration.Attributes;

public class DataRecord
{
    [Name("Time of Day")]
    public string TimeOfDay { get; set; }

    [Name("Process Name")]
    public string ProcessName { get; set; }

    public string PID { get; set; }
    public string Operation { get; set; }
    public string Path { get; set; }
    public string Result { get; set; }
    public string Detail { get; set; }

    [Name("Image Path")]
    public string ImagePath { get; set; }

    public static IEnumerable<DataRecord> ParseDataRecords(Stream file)
    {
        using (var sr = new StreamReader(file))
        using (var csv = new CsvReader(sr))
        {
            foreach (var record in csv.GetRecords<DataRecord>())
            {
                yield return record;
            }
        }
    }
}
person JPK    schedule 02.04.2019