Импортировать файл CSV в c # - PullRequest
2 голосов
/ 02 июня 2009

Я создаю веб-сайт, и одно из требований состоит в том, чтобы пользователи экспортировали свои контакты из своего почтового клиента для импорта их на сайт.

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

Я ищу только основные почтовые клиенты / адресные книги (Outlook, Apple Mail, Entourage, Thunderbird). Все они имеют совершенно другой формат. Entourage использует табуляцию в качестве разделителя, в то время как остальные используют запятую и т. Д. Мне нужно только вынуть адрес электронной почты и (если доступно) имя. Имя становится сложнее, так как некоторые клиенты имеют отдельные поля для имени / фамилии.

Использование FileHelpers было бы идеальным, но, похоже, мне нужно знать структуру csv, прежде чем я смогу найти решение. Я бы не стал писать свой собственный парсер csv, если это возможно.

Вот мои мысли для коллективного улья:

План A

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

План B

  • Запрашивать у пользователя тип почтового клиента и индивидуально кодировать его для каждого отдельного клиента <- кажется действительно неуклюжим. </li>

План C

.... Использовать / купить существующий компонент, который уже делает это ?! (Я уверен, что не могу найти один !!)

Мысли

Ответы [ 6 ]

7 голосов
/ 02 июня 2009

Я бы пошел с Планом Б (и я не согласен, что он неуклюжий).

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

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

Даже если вы используете пользовательскую библиотеку, такую ​​как FileHelpers, вы сможете выполнить ее, передав тип разделителя.

Мне кажется, что вы не должны полагаться на относительный подсчет возможных разделителей, чтобы определить, что это за фактический разделитель (как в плане А).

Редактировать: Еще одна опция, которая только что пришла в голову, - это обеспечить своего рода интерфейс опций, как это делает MS Excel. Вы можете выбрать символ-разделитель с предварительным просмотром того, как данные будут анализироваться в соответствии с выбором.

2 голосов
/ 02 июня 2009

Я бы сначала посмотрел, как это делает конкуренция.

Google: "Мы поддерживаем импорт контактов в формате файла CSV (значения, разделенные запятыми). Для достижения наилучших результатов используйте файл CSV, созданный Outlook, Outlook Express, Yahoo !, или Hotmail. Для адресной книги Apple существует полезная утилита под названием «от A до G». "
Поэтому я полагаю, что они используют ваш план А и проводят проверки для указанных выше файлов CSV.

Live mail / hotmail: Они поддерживают ваш вариант B и поддерживают: Microsoft Outlook (с использованием CSV), Outlook Express (с использованием CSV), контакты Windows, Windows Live Hotmail, Yahoo! Почта (в формате Outlook CSV и через запятую), Gmail (в формате Outlook CSV)

Facebook: Они позволяют вам ввести ваш адрес электронной почты, и, если они его знают (Yahoo, Gmail, Hotmail и т. Д.), Они попросят вас ввести пароль и автоматически получат ваши контакты. (вариант D) Если они не поддерживают вашего провайдера электронной почты, вы все равно можете загрузить файл CSV из Outlook или других форматов (например, вариант B).

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

1 голос
/ 04 ноября 2011

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

GenericConnection connection = new GenericConnection();
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"" +

file.DirectoryName + "\"; Extended Properties='text;HDR=Yes;FMT=" + strDelimiter + "(,)';");
connection.DBConn = con;
connection.Filename = strFilePath;

FileInfo file = new FileInfo(conn.Filename);

DataTable dt = new DataTable();

string selectFields = "Name, email";

using (OleDbCommand cmd = new OleDbCommand(string.Format("SELECT {0} FROM [{1}]", selectFields, file.Name), (OleDbConnection)conn.DBConn))
{
    conn.DBConn.Open();
    using (OleDbDataAdapter adp = new OleDbDataAdapter(cmd))
    {
       adp.Fill(dt);
    }
}
0 голосов
/ 02 июня 2009

План А кажется разумным. Я не думаю, что было бы слишком много имен полей (если они есть) с запятыми или табуляцией. Таким образом, статистика будет точной в 90% случаев. Если статистика достаточно «близка» (например, 15 запятых и 12 вкладок), вы можете сделать следующее:

int i = line.IndexOf("email", StringCompareOptions.CultureInvariantIgnoreCase);
if(i == -1) i = line.IndexOf("e-mail", StringCompareOptions.CultureInvariantIgnoreCase);
else i += 5; // Length of "email"
if(i == -1) throw new Exception("You should select the email field when exporting.");
else i += 6; // Length of "e-mail"

// Find the next delimeter.
string delim = null;
for(int k = i; k < line.Count; k++)
{
    char c = line[k];
    if(c == '\t' || c == ',')
    {
       delim = c.ToString();
       break;
    }
}

if(delim == null)
   throw new Exception("Unrecognised file format.");

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

interface IField
{
    string[] Accepts { get; } // Gets the fields that this can accept.
    string[] Gives { get; } // Gets the field that this would give.

    IEnumerable<KeyValuePair<string, string>> Handle(IEnumerable<KeyValuePair<string, string>> fields);
}

class NameField
{
    string[] Accepts { get return new string[] { "FirstName", "LastName", "Name", "First Name", etc. }; }
    string[] Gives { get return new string[] { "FirstName", "LastName" }; }

    IEnumerable<KeyValuePair<string, string>> Handle(IEnumerable<KeyValuePair<string, string>> fields)
    {
       string firstName = null, lastName = null;
       foreach(KeyValuePair<string, string> field in fields)
       {
           switch(field.Key)
           {
                  case "FirstName":
                  case "First Name":
                  firstName = field.Value;
                  break;
                  // ...
                  case "FullName":
                  case "Full Name":
                  // Split into fn and ln.
                  break;
                  // ...
           }
       }
       yield return new KeyValuePair<string, string>("FirstName", firstName);
       yield return new KeyValuePair<string, string>("LastName", lastName);
    }
}

В любом случае, я уверен, что вы поняли идею. Группа преобразований, которые превратят поля в распознанные.

0 голосов
/ 02 июня 2009

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

вам нужно ограничить символы не буквенно-цифровыми A-z 0-9 "и посмотреть на символ

тогда вы можете определить разделитель. Также имейте в виду, что если поле имеет значение null, некоторые программы не экспортируют «ячейку», например MS Office 2007

0 голосов
/ 02 июня 2009

Возможно, имеет смысл создать интерфейс, такой как «IContactImporter», у которого есть метод «Импорт (Файл / что угодно ...)». Затем для каждого типа контактного файла создайте классы, которые реализуют метод импорта для обработки каждого формата.

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

Для реальных реализаций я бы нашел существующую библиотеку CSV и настроил ее соответственно для каждого формата. Кто-то на моей работе использовал LINQtoCSV , но я не уверен, что есть лучшие варианты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...