Библиотека LINQ to CSV

ОГЛАВЛЕНИЕ

Введение

Эта библиотека позволяет легко использовать файлы CSV с запросами LINQ. Ее возможности включают в себя:

  • Соответствует основным правилам файлов CSV. Правильно обрабатывает поля данных, которые содержат запятые и переносы строк.
  • Кроме запятых, можно использовать многие разделительные символы, включая табуляцию для полей с разделителями табуляции.
  • Можно использовать с IEnumarable анонимного класса, который всегда возвращается посредством запроса LINQ.
  • Поддерживает задержанное чтение.
  • Поддерживает обработку файлов с международными форматами даты и числа.
  • Поддерживает различные кодировки символов.
  • Распознает широкий диапазон форматов даты и времени при чтении файлов.
  • Обеспечивает точную настройку форматов даты и времени при записи файлов.
  • Надежная обработка ошибок, позволяющая вам быстро находить и устранять проблемы в больших файлах исходных данных.

Требования

  • Чтобы компилировать эту библиотеку, необходим компилятор C# 2008, такой как Visual Studio 2008 или Visual C# 2008 Express Edition.
  • Чтобы выполнить код библиотеки, необходимо иметь установленный каркас приложений .NET 3.5.

Установка

  1. Загрузите файл архива с исходным кодом и распакуйте его в какой-либо каталог.
  2. Откройте файл LINQtoCSV\Sources\Source.sln в Visual Studio.
  3. Вы обнаружите, что исходники организованы в виде решения, имеющего такие элементы:
    1. Проект LINQtoCSV в текущей библиотеке.
    2. Проект SampleCode содержит пример кода, показанный в этой статье.
    3. Проект TestConsoleApplication - это работающее консольное приложение, которое демонстрирует большую часть возможностей библиотеки. Код хорошо документирован.
    4. Директория TestFiles внутри проекта TestConsoleApplication содержит тестовые файлы – в формате CSV (с разделением запятыми) и в формате с разделителями табуляции, оба с датами и числами в американском и международном (голландском) формате.
       4.    Скомпилируйте решение. В результате этого появится файл LINQtoCSV.dll в директории  
              Sources\LINQtoCSV\bin. Вам этот файл понадобится при использовании библиотеки в ваших
              собственных  проектах
 

Быстрое начало

Чтение из файла

  1. В вашем проекте добавьте ссылку на LINQtoCSV.dll, которую вы сгенерировали во время установки.
  2. Файл будет прочитан в IEnumerable<T>, где T – это класс данных, который вы определили. Записи данных, прочитанные из файла, будут сохранены в объектах этого класса данных. Вы можете определить класс данных с помощью такого кода:
     using LINQtoCSV;
    using System;

class Product
{
    [CsvColumn(Name = "ProductName", FieldIndex = 1)]
    public string Name { get; set; }

    [CsvColumn(FieldIndex = 2, OutputFormat = "dd MMM HH:mm:ss")]
    public DateTime LaunchDate { get; set; }

    [CsvColumn(FieldIndex = 3, CanBeNull = false, OutputFormat = "C")]
    public decimal Price { get; set; }

    [CsvColumn(FieldIndex = 4)]
    public string Country { get; set; }

    [CsvColumn(FieldIndex = 5)]
    public string Description { get; set; }
}

Имея это определение, вы можете читать из IEnumerable<Product>.

Хотя этот пример только использует свойства, методы библиотеки также распознают простые поля. Только убедитесь, что ваши поля/свойства общедоступные.

Дополнительный атрибут CsvColumn позволяет вам определить, когда поле/свойство обязательное, как оно должно записываться в выходной файл и т.д.

       3.    Импортируйте пространство имен LINQtoCSV в начало исходного файла, откуда вы будете читать файл:
using LINQtoCSV;
       4.   Создайте объект CsvFileDescription и присвойте ему в качестве начальных значений параметры файла, который вы собираетесь читать. Это будет выглядеть примерно так:
    CsvFileDescription inputFileDescription = new CsvFileDescription
{
   SeparatorChar = ',',
    FirstLineHasColumnNames = true
};

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

      5.   Создайте объект CsvContext:
CsvContext cc = new CsvContext();

Это объект, который представляет методы Read и Write, которые вы будете использовать для чтения и записи файлов.

      6.    Прочитайте файл в IEnumerable<T>, используя метод Read объекта CsvContext, примерно так:
IEnumerable<Product> products =
    cc.Read<Product>("products.csv", inputFileDescription);

Этот код читает файл products.csv в переменную products, которая имеет тип IEnumerable<Product>.

      7.    Теперь вы можете получить доступ к переменной products через запрос LINQ, цикл foreach и т.д.:
var productsByName =
    from p in products
    orderby p.Name
    select new { p.Name, p.LaunchDate, p.Price, p.Description };

// или ...
foreach (Product item in products) { .... }

Ниже снова приводится код, читающий из файла, но теперь за один раз:

CsvFileDescription inputFileDescription = new CsvFileDescription
{
    SeparatorChar = ',',
    FirstLineHasColumnNames = true
};

CsvContext cc = new CsvContext();

IEnumerable<Product> products =
    cc.Read<Product>("products.csv", inputFileDescription);

// Данные сейчас доступны через переменную products.

var productsByName =
    from p in products
    orderby p.Name
    select new { p.Name, p.LaunchDate, p.Price, p.Description };

// или ...
foreach (Product item in products) { .... }

Вы можете найти тот же самый код в проекте SampleCode в исходниках.

Запись в файл

Выполняется аналогично чтению из файла.

  1. В вашем проекте добавьте ссылку на LINQtoCSV.dll.
  2. Метод Write принимает IEnumerable<T> и записывает каждый объект типа T в IEnumerable<T> как запись данных в файл. Определение вашего класса данных может выглядеть так:
using LINQtoCSV;
using System;

class Product
{
    [CsvColumn(Name = "ProductName", FieldIndex = 1)]
    public string Name { get; set; }

    [CsvColumn(FieldIndex = 2, OutputFormat = "dd MMM HH:mm:ss")]
    public DateTime LaunchDate { get; set; }

    [CsvColumn(FieldIndex = 3, CanBeNull = false, OutputFormat = "C")]
    public decimal Price { get; set; }

    [CsvColumn(FieldIndex = 4)]
    public string Country { get; set; }

    [CsvColumn(FieldIndex = 5)]
    public string Description { get; set; }
}

Дополнительный атрибут CsvColumn позволяет вам определить, какие форматы даты и чисел использовать при записи каждого поля данных. Подробное описание свойств CsvColumnCanBeNull, OutputFormat, и т.д.) доступно здесь. (

Хотя этот пример использует только свойства, вы также можете использовать простые поля.

Метод Write может успешно использовать анонимный тип для T, поэтому вы можете записать вывод запроса LINQ прямо в файл. В этом случае вы не определяете T сами. Позже будет приведен пример этого.

     3.    Импортируйте пространство имен LINQtoCSV в начало исходного файла, куда вы будете записывать файл:

using LINQtoCSV;
      4.   Убедитесь, что данные сохранились в объект, который реализует IEnumerable<T>, такой как List<T>, или IEnumerable<T>, возвращаемый методом Read.
List<Product> products2 = new List<Product>();
// Заполняем список продуктами
// ...
     5.   Создайте объект CsvFileDescription, и инициалзируйте его с данными о файле, который вы будете записывать, в следующих строках:
CsvFileDescription outputFileDescription = new CsvFileDescription
{
    SeparatorChar = '\t', // с разделителями табуляции
    FirstLineHasColumnNames = false, // в первой записи нет имен столбцов
    FileCultureName = "nl-NL" // применяем формат, используемый в Голландии
};
     6.    Создайте объект CsvContext:
CsvContext cc = new CsvContext();
     7.    Вызовите метод Write, предоставляемый объектом CsvContext, чтобы записать содержимое вашегоIEnumerable<T> в файл:
cc.Write(
    products2,
    "products2.csv",
    outputFileDescription);

Этот код записывает объекты Product в переменной products2 в файл "products2.csv".

Этот код также записывает файл, но за один раз:

List<Product> products2 = new List<Product>();
// Заполняем список продуктами
// ...

CsvFileDescription outputFileDescription = new CsvFileDescription
{
    SeparatorChar = '\t', // с разделителями табуляции
    FirstLineHasColumnNames = false, // в первой записи нет имен столбцов
    FileCultureName = "nl-NL" // применяем формат, используемый в Голландии
};

CsvContext cc = new CsvContext();

cc.Write(
    products2,
    "products2.csv",
    outputFileDescription);   

Написание IEnumerable анонимного типа

Если у вас есть запрос LINQ, порождающий IEnumerable анонимного типа, записать этот IEnumerable в файл не сложно:

CsvFileDescription outputFileDescription = new CsvFileDescription
{
.....
};

CsvContext cc = new CsvContext();

// запрос LINQ, возвращающий IEnumerable анонимного типа
// в переменную productsNetherlands
var productsNetherlands =
    from p in products
    where p.Country == "Netherlands"
    select new { p.Name, p.LaunchDate, p.Price, p.Description };

// записываем содержимое переменной productsNetherlands в файл
cc.Write(
    productsNetherlands,
    "products-Netherlands.csv",
    outputFileDescription);

Здесь запрос LINQ выбирает все продукты для "Голландии" из переменной products и возвращает IEnumerable, содержащий объекты какого-либо анонимного типа, который имеет поля Name, LaunchDate, Price и Description. Затем метод Write записывает эти объекты в файл products-Netherlands.csv.


CsvContext.Write Overloads

  • Write<T>(IEnumerable<T> values, string fileName)
  • Write<T>(IEnumerable<T> values, string fileName, CsvFileDescription fileDescription)
  • Write<T>(IEnumerable<T> values, TextWriter stream)
  • Write<T>(IEnumerable<T> values, TextWriter stream, CsvFileDescription fileDescription)

Несколько интересных фактов об этих перегрузках:

  • Ни одна из перегрузок не возвращает значение.
  • В отличие от метода Read, Write не требует, чтобы T имел конструктор без параметров.
  • Перегрузки, принимающие поток, записывают данные в поток. Перегрузка, которая принимает имя файла, записывает данные в файл.
  • Перегрузки, которые не принимают объект CsvFileDescription, просто создают его сами, используя значения по умолчанию для свойств CsvFileDescription.

Перегрузки CsvContext.Read

  • Read<T>(string fileName)
  • Read<T>(string fileName, CsvFileDescription fileDescription)
  • Read<T>(StreamReader stream)
  • Read<T>(StreamReader stream, CsvFileDescription fileDescription)

Несколько интересных фактов об этих перегрузках:

  • Каждая перегрузка возвращает IEnumerable<T>.
  • T должен иметь конструктор без параметров. Если вы не определили конструктор для T, компилятор сам сгенерирует конструктор без параметров.
  • Перегрузки, принимающие поток, читают данные из потока. Перегрузки, принимающие имя файла, читают данные из файла. Также смотрите раздел про задержанное чтение.
  • Перегрузки, не принимающие объект CsvFileDescription, просто создают его сами, используя значения по умолчанию для свойств CsvFileDescription.

Задержанное чтение

Здесь описано, как перегрузки Read реализуют задержанное чтение:

  • Когда вы вызываете метод Read (который возвращает IEnumerable<T>), никакие данные еще не читаются. Если используется файл, файл еще не открыт.
  • Когда перечислитель извлекается из IEnumerable<T> (например, при начале цикла foreach), файл открывается для чтения. Если используется поток, поток перематывается (ищется начало потока).
  • Каждый раз, когда вы извлекаете новый объект из перечислителя (например, при обходе с помощью цикла foreach), новая запись читается из файла или потока.
  • Когда вы закрываете перечислитель (например, когда завершается цикл foreach, или когда вы его прерываете), файл закрывается. Если используется поток, он остается без изменений.

Это означает, что:

  • При чтении из файла файл открывается для чтения, когда вы обращаетесь к IEnumerable<T> в цикле foreach.
  • Файл может обновляться между сеансами доступа. Вы можете получить доступ к IEnumerable<T> в цикле foreach, затем обновить файл, затем снова получить доступ к IEnumerable<T> в цикле foreach,чтобы извлечь новые данные, и т.д. Вам нужно вызвать Read только один раз в начале, чтобы получить IEnumerable<T>.

 

CsvFileDescription

Методам Read и Write нужны параметры файла, из которого они читают и в который записывают, такие как, содержит ли первая запись названия столбцов.

Как показано в примерах Чтение из файла и Запись в файл, эти параметры помещаются в объект с типом CsvFileDescription, который вы затем передаете методу Read или Write. Это избавляет от использования длинного списка параметров и позволяет использовать одинаковые параметры для многих файлов.

Объект CsvFileDescription имеет такие свойства:

  • SeparatorChar
  • QuoteAllFields
  • FirstLineHasColumnNames
  • EnforceCsvColumnAttribute
  • FileCultureName
  • TextEncoding
  • DetectEncodingFromByteOrderMarks
  • MaximumNbrExceptions

SeparatorChar

Тип:

char (символьный)

Значение по умолчанию:

','

Применяется:

Чтение и запись

Пример:

CsvFileDescription fd = new CsvFileDescription();
fd.SeparatorChar = '\t'; // используется файл с разделителями табуляции

CsvContext cc = new CsvContext();
cc.Write(data, "file.csv", fd);

Символ, используемый для разделения полей в файле. Это может быть запятая для файлов CSV или '\t' для файла с разделителями табуляции.

Вы можете использовать любой символ, за исключением пробельных символов или двойных кавычек (").

QuoteAllFields

Тип:

Bool (логический)

Значение по умолчанию:

False (ложь)

Применяется:

Только запись

Пример:

fd.QuoteAllFields = true; // вынуждает запрос обрабатывать все поля

Когда установлено значение ложь, метод Write только лишь заключает поля данных в кавычки, когда требуется, чтобы избежать путаницы - например, когда поле содержит SeparatorChar или разрыв строки.

Когда установлено значение истина, Write заключает все поля данных в кавычки.

FirstLineHasColumnNames

Тип:

bool (логический)

Значение по умолчанию:

true (истина)

Применяется:

Чтение и запись

Пример:

fd.FirstLineHasColumnNames = false; // первая запись не содержит заголовков столбца

При чтении файла сообщите методу Read, считать ли поля данных в первой записи файла заголовками столбцов.

При записи файла сообщите методу Write, записывать ли заголовки столбцов как первую запись файла.

EnforceCsvColumnAttribute

Тип:

bool (логический)

Значение по умолчанию:

false (ложь)

Применяется:

Чтение и запись

Пример:

fd.EnforceCsvColumnAttribute = true; // используются только поля с атрибутом [CsvColumn] 

Когда установлено значение истина,  метод Read читает только поля данных в общедоступных полях и свойствах с атрибутом [CsvColumn], игнорируя все другие поля и свойства. И Write записывает содержимое только в общедоступные поля и свойства с атрибутом [CsvColumn].

Когда установлено значение ложь, используются все общедоступные поля и свойства.

FileCultureName

Тип:

string (строка)

Значение по умолчанию:

Текущие системные настройки

Применяется:

Чтение и запись

Пример:

fd.FileCultureName = "en-US"; // используется американский формат даты и чисел

Различные культуры используют различные способы записи дат и чисел. 23 Мая 2008 - это 5/23/2008 в США (en-US) и 23/5/2008 в Германии (de-DE). Используйте поле FileCultureName, чтобы сообщить методу Read,Write, как записывать даты и числа в файл. как интерпретировать даты и числа, читаемые из файла, и сообщить

По умолчанию библиотека использует текущие настройки языка/страны в вашей системе. Если ваша система использует Французско-Канадская (fr-CA), библиотека использует эту культуру до тех пор, пока вы не замените ее в параметре FileCultureName.

Библиотека использует такие же имена культур, что и класс .NET "CultureInfo".

TextEncoding

Тип:

Кодировка

Значение по умолчанию:

Encoding.UTF8

Применяется:

Чтение и запись

Пример:

fd.TextEncoding = Encoding.Unicode; // используется кодировка Unicode 

Если вы читаете или записываете файлы на английском языке, нет необходимости устанавливать TextEncoding.

Однако если вы используете другие языки, кроме английского, кодировка символов в ваших файлах может быть проблемой. Вы захотите убедиться, что кодировка, используемая библиотекой, соответствует кодировке, используемой любыми другими программами (редакторами, электронными таблицами), которые имеют доступ к вашим файлам.

Например, если вы записываете файлы с символом евро, вам может понадобиться кодировка Unicode, как показано в примере.

DetectEncodingFromByteOrderMarks

Тип:

bool (логический)

Значение по умолчанию:

true (истина)

Применяется:

Только чтение

Пример:

fd.DetectEncodingFromByteOrderMarks = false; // подавляет выявление кодировки

Связано с TextEncoding. Обычно работает нормально со значением по умолчанию.

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

MaximumNbrExceptions

Тип:

int (целый)

Значение по умолчанию:

100

Применяется:

Только чтение

Пример:

fd.MaximumNbrExceptions = -1; // всегда читает файл полностью перед порождением исключения AggregatedException

Устанавливает максимальное число исключений, которое может быть собрано в AggregatedException.

Чтобы не устанавливать ограничений и читать весь файл независимо от того, сколько вы получили исключений, установите AggregatedException в -1.

Более подробную информацию о собранных исключениях смотрите в разделе «Обработка ошибок».


Атрибут CsvColumn

Как показано в примерах Чтение из файла и Запись в файл, вы можете дополнить общедоступные поля и свойства вашего класса данных атрибутом CsvColumn,чтобы определить такие вещи, как формат вывода для даты и числовых полей.

Использовать атрибут CsvColumn необязательно. Пока свойство EnforceCsvColumnAttribute объекта CsvFileDescription, который вы передаете в метод Read или Write, имеет значение ложь, эти методы будут проверять все общедоступные поля и свойства в классе данных. Они будут просто использовать значения по умолчанию, показанные для каждого свойства CsvColumn ниже.

Атрибут CsvColumn имеет такие свойства:

  • Name
  • CanBeNull
  • NumberStyle
  • OutputFormat
  • FieldIndex

Name

Тип:

string (строковый)

Значение по умолчанию:

Имя поля или свойства

Применяется:

Чтение и запись

Пример:

[CsvColumn(Name = "StartDate")]
public DateTime LaunchDate { get; set; }

Методы Read и Write обычно предполагают, что поля данных в файле имеют такие же имена, что и соответствующие поля или свойства в классе. Используйте свойство Name, чтобы определить другое имя для поля данных.

CanBeNull

Тип:

bool (логический)

Значение по умолчанию:

true (истина)

Применяется:

Только чтение

[CsvColumn(CanBeNull = false)]
public DateTime LaunchDate { get; set; }

Если установлено значение ложь, и запись во входном файле не имеет значения для этого поля или свойства, то метод Read генерирует исключение MissingRequiredFieldException.

FieldIndex

Тип:

bool (логический)

Значение по умолчанию:

Int32.MaxValue

Применяется:

Только чтение

Пример:

[CsvColumn(FieldIndex = 1)]
public DateTime LaunchDate { get; set; }

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

Чтение - Методу Read необходимо как-то связать поля данных во входном файле с полями и свойствами в классе данных. Если в первой записи файла содержатся имена столбцов, то Read просто устанавливает соответствие имен столбцов с именами полей и свойств в классе данных.

Но если в первой записи файла нет имен столбцов, Read необходимо изучить порядок полей данных в записях данных, чтобы установить их соответствие с полями и свойствами в классе данных. К сожалению, интегрированная среда .NET не предоставляет способа надежно извлечь этот порядок из определения класса. Поэтому вы должны определить, какое поле/свойство предшествует какому полю/свойству, передав поля и свойства атрибута CsvColumn свойству FieldIndex.

FieldIndex не должны начинаться с 1. Они не должны быть последовательными. Методы Read и Write просто будут предполагать, что поле/свойство предшествует какому-то другому полю/свойству, если его FieldIndex меньше.

Запись - Метод Write использует FieldIndex каждого поля или свойства, чтобы выяснить, в каком порядке записывать поля данных в выходной файл. Поля и свойства без FieldIndex записываются последними, в произвольном порядке.

NumberStyle

Тип:

NumberStyles

Значение по умолчанию:

NumberStyles.Any

Применяется к:

Только чтение числовых полей

Пример:

[CsvColumn(NumberStyle = NumberStyles.HexNumber)]
public DateTime LaunchDate { get; set; }

Позволяет вам определить, какие форматы чисел допустимы во входном файле (список параметров).

По умолчанию разрешены все форматы, за исключением одного особого случая. Чтобы принимать шестнадцатеричные числа, не начинающиеся с 0x, используйте NumberStyles.HexNumber, как показано в примере.

OutputFormat

Тип:

строковый

Значение по умолчанию:

"G"

Применяется к:

Только запись

Пример:

[CsvColumn(OutputFormat = "dd MMM yy")]
public DateTime LaunchDate { get; set; }

Давайте установим формат вывода чисел и дат/времени. Формат по умолчанию "G" хорошо работает для дат и чисел большую часть времени.

При записи даты/времени или числового поля метод Write сначала определяет тип поля (DateTime, десятичный, вещественное число двойной точности и т.д.) и затем вызывает метод ToString для этого типа с данным значением OutputFormat. В вышеприведенном примере, если LaunchDate равняется 23 ноября 2008, поле, записанное в файл, будет содержать "23 Nov 08".

При наличии множества форматов конечный результат зависит от языка/страны файла, как установлено в свойстве FileCultureName объекта CsvFileDescription. Если LaunchDate равняется 23 ноября 2008, и вы задали укороченный формат даты:

[CsvColumn(OutputFormat = "d")]
public DateTime LaunchDate { get; set; }

Итоговое значение, записанное в выходной файл, будет равняться "11/23/08", если вы используете американские даты (FileCultureName установлено в "en-US"), и будет "23/11/08", если вы используете немецкие даты (FileCultureName установлено в "de-DE").

  • Код формата для DateTime
  • Стандартные строки формата чисел  
  • Клиентские строки формата чисел  


Обработка ошибок

  • Исключение
    • LINQtoCSVException
      • BadStreamException
      • CsvColumnAttributeRequiredException
      • DuplicateFieldIndexException
      • RequiredButMissingFieldIndexException
      • ToBeWrittenButMissingFieldIndexException
      • NameNotInTypeException
      • MissingCsvColumnAttributeException
      • TooManyDataFieldsException
      • TooManyNonCsvColumnDataFieldsException
      • MissingFieldIndexException
      • MissingRequiredFieldException
      • WrongDataFormatException
      • AggregatedException

Когда методы Read и Write обнаруживают ситуацию, связанную с появлением ошибки, они порождают исключение со всей информацией, которая необходима для решения проблемы. Все исключения унаследованы от класса .NET Exception.

Извлечение информации об ошибке

Дополнительно к таким свойствам, как StackTrace и Message, класс Exception имеет свойство Data. Методы Read и Write используют это свойство, чтобы предоставить информацию об исключении таким образом, чтобы ее можно было легко прочитать для вашего кода, тогда как они предоставляют сообщения об ошибках, предназначенные для людей, через свойство Message.

Описание для каждого исключения (ниже) показывает, какая информация сохраняется в свойстве Data.

Группирование исключений

Когда метод Read обнаруживает ошибку во время чтения данных из файла, он не порождает исключение, но сохраняет его в списке, имеющем тип List<Exception>. После обработки файла метод порождает единственное исключение типа AggregatedException, со списком исключений в его свойстве Data["InnerExceptionsList"]. Это позволяет вам устранить все ошибки во входном файле за раз, вместо их устранения одна за другой.

Вы можете ограничить число исключений, которые собираются, таким образом, установив свойство MaximumNbrExceptions объекта CsvFileDescription, который вы передаете методу Read. По умолчанию, MaximumNbrExceptions установлен в 100. Когда предел достигается, AggregatedException генерируется со списком собранных до текущего момента исключений.

Не все исключения собираются! Перед тем как Read начнет читать данные из файла, он сначала обрабатывает имена столбцов, атрибуты CsvColumn и т.д. Если происходит ошибка во время предварительного этапа, метод порождает исключение.

Задержанное чтение

Из-за задержанного чтения вы будете получать исключения не только при вызове метода Read, но также при доступе к IEnumerable<T>, который возвращается методом Read.

Пример

Следующий код читает файл и обрабатывает исключения. Чтобы показать, как используется свойство Data, он содержит специальную обработку исключения DuplicateFieldIndexException – порождается, когда методы Read и Write обнаруживают два поля или свойства с одинаковым FieldIndex.

public static void ShowErrorMessage(string errorMessage)
{
    // показываем сообщение об ошибке пользователю
    // .....
}

public static void ReadFileWithExceptionHandling()
{
    try
    {
        CsvContext cc = new CsvContext();

        CsvFileDescription inputFileDescription = new CsvFileDescription
        {
            MaximumNbrExceptions = 50
            // ограничиваем число группируемых исключений до 50
        };

        IEnumerable<Product> products =
            cc.Read<Product>("products.csv", inputFileDescription);

        // Выполняем обработку данных
        // ...........

    }
    catch(AggregatedException ae)
    {
        // Обрабатываем все исключения, сгенерированные во время обработки файла

        List<Exception> innerExceptionsList =
            (List<Exception>)ae.Data["InnerExceptionsList"];

        foreach (Exception e in innerExceptionsList)
        {
            ShowErrorMessage(e.Message);
        }
    }
    catch(DuplicateFieldIndexException dfie)
    {
        // имя класса, используемого в методе Read – в этом случае "Продукт"
        string typeName = Convert.ToString(dfie.Data["TypeName"]);

        // имена двух полей или свойств, имеющих одинаковый FieldIndex
        string fieldName = Convert.ToString(dfie.Data["FieldName"]);
        string fieldName2 = Convert.ToString(dfie.Data["FieldName2"]);

        // Действительный FieldIndex, который является общим для двух полей
        int commonFieldIndex = Convert.ToInt32(dfie.Data["Index"]);

        // Как-то обрабатываем эту информацию
        // .........


        // сообщаем пользователю об ошибке
        ShowErrorMessage(dfie.Message);
    }
    catch(Exception e)
    {
        ShowErrorMessage(e.Message);
    }
}

BadStreamException

Это исключение имеет такие же свойства, что и Exception.

Порождается, когда поток, переданный Read, равняется нулю или не поддерживает Seek (поиск). Поток должен поддерживать Seek, в противном случае его нельзя перемотать при доступе к IEnumarable,возвращенному методом Read.

CsvColumnAttributeRequiredException

Это исключение имеет такие же свойства, что и Exception.

Порождается, когда объект CsvFileDescription, переданный Read, имеет FirstLineHasColumnNames и EnforceCsvColumnAttribute, установленные в ложь.

Если в файле нет имен столбцов, то Read основывается на FieldIndex каждого поля или свойства в классе данных, чтобы установить их соответствие с полями данных в файле. Но если EnforceCsvColumnAttributeустановлен в ложь, то предполагается, что поля или свойства без атрибута CsvColumn также могут использоваться для приема данных, хотя они не имеют FieldIndex.

DuplicateFieldIndexException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с неправильными полями/свойствами

Data["FieldName"]

string (строковый)

Поля или свойства с одинаковым FieldIndex

Data["FieldName2"]

Data["Index"]

int (целый)

Общий FieldIndex

Порождается, когда два или более полей или свойств имеют одинаковый FieldIndex.

RequiredButMissingFieldIndexException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с неправильным полем/свойством

Data["FieldName"]

string (строковый)

Поле или свойство без FieldIndex

Когда в первой записи  файла нет имен столбцов (FirstLineHasColumnNames равно ложь), каждое обязательное поле (атрибут CanBeNull установлен в ложь) должно иметь атрибут FieldIndex, в противном случае его нельзя прочитать из файла.

ToBeWrittenButMissingFieldIndexException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с неправильным полем/свойством

Data["FieldName"]

string (строковый)

Поле или свойство без FieldIndex

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

Когда метод Write имеет CsvFileDescription с FirstLineHasColumnNames, установленным в ложь, и находит поле или свойство, которое не имеет FieldIndex, он порождает ToBeWrittenButMissingFieldIndexException.

NameNotInTypeException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с пропущенным полем/свойством

Data["FieldName"]

string (строковый)

Поле или свойство, которое не найдено

Data["FileName"]

string (строковый)

Имя входного файла

Если метод Read имеет CsvFileDescription с FirstLineHasColumnNames, установленным в истину, и одно из имен столбцов в первой записи в файле не соответствует полю или свойству, он порождает NameNotInTypeException.

MissingCsvColumnAttributeException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с неправильным полем/свойством

Data["FieldName"]

string (строковый)

Поле или свойство без атрибута CsvColumn

Data["FileName"]

string (строковый)

Имя входного файла

Метод Read может породить это исключение, когда CsvFileDescription имеет FirstLineHasColumnNames и EnforceCsvColumnAttribute, установленные в истину. Когда Read читает имена столбцов из первой записи, одно из имен столбцов может соответствовать полю или свойству, которое не имеет атрибута CsvColumn, даже если могут использоваться только поля и свойства с атрибутом CsvColumn. Когда это происходит, Read порождает MissingCsvColumnAttributeException.

TooManyDataFieldsException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса данных

Data["LineNbr"]

int (целый)

Строка во входном файле с лишним полем данных

Data["FileName"]

string (строковый)

Имя входного файла

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

TooManyNonCsvColumnDataFieldsException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса данных

Data["LineNbr"]

int (целый)

Строка во входном файле с лишним полем данных

Data["FileName"]

string (строковый)

Имя входного файла

Когда используются только поля или свойства, которые имеют атрибут CsvColumn (Read имеет CsvFileDescription с EnforceCsvColumnAttribute, установленным в истину), и запись во входном файле имеет больше полей данных, чем число полей и свойств с атрибутом CsvColumn, порождается TooManyNonCsvColumnDataFieldsException.

MissingFieldIndexException

Дополнительные свойства - Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса данных

Data["LineNbr"]

int (целый)

Строка с неправильным полем

Data["FileName"]

string (строковый)

Имя входного файла

Если в первой записи входного файла нет имен столбцов (Read имеет CsvFileDescription с FirstLineHasColumnNames, установленным в ложь), то Read основывается на FieldIndex полей и свойств в классе данных, чтобы установить их соответствие полям данных в файле.

Когда запись во входном файле имеет больше полей данных, чем число полей и свойств с FieldIndex в классе данных, порождается MissingFieldIndexException.

MissingRequiredFieldException

Дополнительные свойства – Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с обязательным полем/свойством

Data["FieldName"]

string (строковый)

Имя обязательного поля/свойства

Data["LineNbr"]

int (целый)

Строка, где должно быть пропущенное поле

Data["FileName"]

string (строковый)

Имя входного файла

Порождается, когда запись во входном файле не содержит значения для обязательного поля или свойства (свойство CanBeNull атрибута CsvColumn установлено в ложь).

Различие между нулевой и пустой строкой

Пустые строки и строки, состоящие только из пробела, могут быть заключены в кавычки, поэтому они распознаются как отличные от нуля.

Эти входные строки имеют поля данных "abc", нуль и "def":

abc,,def
abc,   ,def

В то время как эта строка имеет поле данных "abc", за которым следует пустая строка, за которой следует "def":

abc,"",def

и эта линия имеет поле данных "abc", за которым следует строка с тремя пробелами, за которой следует "def":

abc,"   ",def

WrongDataFormatException

Дополнительные свойства – Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса с полем/свойством

Data["FieldName"]

string (строковый)

Имя поля/свойства

Data["FieldValue"]

string (строковый)

Неверное значение данных

Data["LineNbr"]

int (целый)

Строка с неверным значением данных

Data["FileName"]

string (строковый)

Имя входного файла

Порождается, когда поле имеет неверный формат. Например, числовое поле содержит значение "abc".

AggregatedException

Дополнительные свойства – Это исключение имеет такие же свойства, что и Exception, а также ряд дополнительных свойств:

Свойство

Тип

Описание

Data["TypeName"]

string (строковый)

Имя класса данных, используемого Read

Data["FileName"]

string (строковый)

Имя входного файла

Data["InnerExceptionsList"]

List<Exception>

Список Exception (исключений)

Используется для группировки исключений, сгенерированных при чтении файла.

Автор: Matt Perdeck

Загрузить исходный код - 43.2 KB