12 ответов:
Microsoft TextFieldParser стабилен и следует RFC 4180 CSV-файлов. Не пугайтесь
Microsoft.VisualBasicпространство имен; это стандартный компонент в .NET Framework, просто добавьте ссылку на глобальныйMicrosoft.VisualBasicсборка.Если вы компилируете для Windows (в отличие от Mono) и не ожидаете, что вам придется анализировать "сломанные" (не совместимые с RFC) CSV-файлы, то это будет очевидный выбор, поскольку он бесплатный, неограниченный, стабильный и активный поддерживается, большинство из которых не может быть сказано для FileHelpers.
Читайте также: как читать из текстовых файлов с разделителями-запятыми в Visual Basic для примера кода VB.
используйте соединение OleDB.
String sConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\InputDirectory\;Extended Properties='text;HDR=Yes;FMT=Delimited'"; OleDbConnection objConn = new OleDbConnection(sConnectionString); objConn.Open(); DataTable dt = new DataTable(); OleDbCommand objCmdSelect = new OleDbCommand("SELECT * FROM file.csv", objConn); OleDbDataAdapter objAdapter1 = new OleDbDataAdapter(); objAdapter1.SelectCommand = objCmdSelect; objAdapter1.Fill(dt); objConn.Close();
Если вы ожидаете довольно сложные сценарии для разбора CSV,даже не думайте сворачивать наш собственный парсер. Есть много отличных инструментов там, как FileHelpers, или даже те из CodeProject.
дело в том, что это довольно распространенная проблема, и вы можете поспорить, что много разработчики программного обеспечения уже подумали и решили эту проблему.
Брайан дает хорошее решение для преобразования его в строго типизированной коллекции.
большинство приведенных методов анализа CSV не учитывают экранирующие поля или некоторые другие тонкости CSV-файлов (например, поля обрезки). Вот код, который я лично использую. Это немного грубо по краям и почти не имеет отчетов об ошибках.
public static IList<IList<string>> Parse(string content) { IList<IList<string>> records = new List<IList<string>>(); StringReader stringReader = new StringReader(content); bool inQoutedString = false; IList<string> record = new List<string>(); StringBuilder fieldBuilder = new StringBuilder(); while (stringReader.Peek() != -1) { char readChar = (char)stringReader.Read(); if (readChar == '\n' || (readChar == '\r' && stringReader.Peek() == '\n')) { // If it's a \r\n combo consume the \n part and throw it away. if (readChar == '\r') { stringReader.Read(); } if (inQoutedString) { if (readChar == '\r') { fieldBuilder.Append('\r'); } fieldBuilder.Append('\n'); } else { record.Add(fieldBuilder.ToString().TrimEnd()); fieldBuilder = new StringBuilder(); records.Add(record); record = new List<string>(); inQoutedString = false; } } else if (fieldBuilder.Length == 0 && !inQoutedString) { if (char.IsWhiteSpace(readChar)) { // Ignore leading whitespace } else if (readChar == '"') { inQoutedString = true; } else if (readChar == ',') { record.Add(fieldBuilder.ToString().TrimEnd()); fieldBuilder = new StringBuilder(); } else { fieldBuilder.Append(readChar); } } else if (readChar == ',') { if (inQoutedString) { fieldBuilder.Append(','); } else { record.Add(fieldBuilder.ToString().TrimEnd()); fieldBuilder = new StringBuilder(); } } else if (readChar == '"') { if (inQoutedString) { if (stringReader.Peek() == '"') { stringReader.Read(); fieldBuilder.Append('"'); } else { inQoutedString = false; } } else { fieldBuilder.Append(readChar); } } else { fieldBuilder.Append(readChar); } } record.Add(fieldBuilder.ToString().TrimEnd()); records.Add(record); return records; }обратите внимание, что это не обрабатывает крайний случай полей, не разделенных двойными кавычками, но meerley имея строку в кавычках внутри него. Смотрите этот пост для немного лучшего расширения, а также некоторые ссылки на некоторые правильные библиотеки.
Я согласен с @Не себя. FileHelpers хорошо протестирован и обрабатывает все виды краевых случаев, с которыми вам в конечном итоге придется иметь дело, если вы сделаете это самостоятельно. Взгляните на то, что делает FileHelpers, и только напишите свой собственный, если вы абсолютно уверены, что либо (1) Вам никогда не нужно будет обрабатывать крайние случаи FileHelpers, либо (2) вы любите писать такие вещи и будете очень рады, когда вам придется разбирать такие вещи, как это:
1, "Билл","Смит", "Супервайзер", "Без Комментариев"
2, ' Дрейк,', 'О'Мэлли', " Дворник,
Ой, меня не цитируют и я на новой линии!
мне было скучно, поэтому я изменил некоторые вещи я писал. Он пытается инкапсулировать синтаксический анализ способом OO, который сокращает количество итераций через файл, он только повторяется один раз в верхнем foreach.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { // usage: // note this wont run as getting streams is not Implemented // but will get you started CSVFileParser fileParser = new CSVFileParser(); // TO Do: configure fileparser PersonParser personParser = new PersonParser(fileParser); List<Person> persons = new List<Person>(); // if the file is large and there is a good way to limit // without having to reparse the whole file you can use a // linq query if you desire foreach (Person person in personParser.GetPersons()) { persons.Add(person); } // now we have a list of Person objects } } public abstract class CSVParser { protected String[] deliniators = { "," }; protected internal IEnumerable<String[]> GetRecords() { Stream stream = GetStream(); StreamReader reader = new StreamReader(stream); String[] aRecord; while (!reader.EndOfStream) { aRecord = reader.ReadLine().Split(deliniators, StringSplitOptions.None); yield return aRecord; } } protected abstract Stream GetStream(); } public class CSVFileParser : CSVParser { // to do: add logic to get a stream from a file protected override Stream GetStream() { throw new NotImplementedException(); } } public class CSVWebParser : CSVParser { // to do: add logic to get a stream from a web request protected override Stream GetStream() { throw new NotImplementedException(); } } public class Person { public String Name { get; set; } public String Address { get; set; } public DateTime DOB { get; set; } } public class PersonParser { public PersonParser(CSVParser parser) { this.Parser = parser; } public CSVParser Parser { get; set; } public IEnumerable<Person> GetPersons() { foreach (String[] record in this.Parser.GetRecords()) { yield return new Person() { Name = record[0], Address = record[1], DOB = DateTime.Parse(record[2]), }; } } } }
есть две статьи о CodeProject, которые предоставляют код для решения, один из которых использует StreamReader и один импорт данных CSV С помощью Текстовый Драйвер Microsoft.
хороший простой способ сделать это-открыть файл и прочитать каждую строку в массив, связанный список, структуру данных по вашему выбору. Однако будьте осторожны с обработкой первой строки.
Это может быть над вашей головой, но, похоже, есть прямой способ получить к ним доступ, а также с помощью строку подключения.
Почему бы не попробовать использовать Python вместо C# или VB? Он имеет хороший модуль CSV для импорта, который делает всю тяжелую работу для вас.
Я должен был использовать CSV-парсер в .NET для проекта этим летом и остановился на драйвере Microsoft Jet Text. Укажите папку с помощью строки подключения, а затем запрос файла с помощью инструкции SQL Select. Вы можете указать сильные типы с помощью схемы.ini-файл. Сначала я этого не делал, но потом я получал плохие результаты, когда тип данных не был сразу очевиден, например, IP-номера или запись типа "XYQ 3.9 SP1".
одно ограничение, с которым я столкнулся, заключается в том, что он не может обрабатывать имена столбцов выше 64 символов; он усекается. Это не должно быть проблемой, за исключением того, что я имел дело с очень плохо спроектированными входными данными. Он возвращает an ADO.NET набор данных.
Это было лучшее решение я нашел. Я бы опасался прокатки моего собственного CSV-парсера, так как я, вероятно, пропустил бы некоторые из конечных случаев, и я не нашел никаких других бесплатных пакетов CSV-парсинга для .NET.
EDIT: кроме того, может быть только одна схема.ini-файл для каждого каталога, поэтому я динамически добавляется к нему, чтобы строго ввести необходимые столбцы. Он будет только строго вводить указанные столбцы и выводить для любого неопределенного поля. Я действительно оценил это, поскольку я имел дело с импортом fluid 70+ column CSV и не хотел указывать каждый столбец, только неверные.
Я набрал какой-то код. Результат в datagridviewer выглядел хорошо. Он анализирует одну строку текста в arraylist объектов.
enum quotestatus { none, firstquote, secondquote } public static System.Collections.ArrayList Parse(string line,string delimiter) { System.Collections.ArrayList ar = new System.Collections.ArrayList(); StringBuilder field = new StringBuilder(); quotestatus status = quotestatus.none; foreach (char ch in line.ToCharArray()) { string chOmsch = "char"; if (ch == Convert.ToChar(delimiter)) { if (status== quotestatus.firstquote) { chOmsch = "char"; } else { chOmsch = "delimiter"; } } if (ch == Convert.ToChar(34)) { chOmsch = "quotes"; if (status == quotestatus.firstquote) { status = quotestatus.secondquote; } if (status == quotestatus.none ) { status = quotestatus.firstquote; } } switch (chOmsch) { case "char": field.Append(ch); break; case "delimiter": ar.Add(field.ToString()); field.Clear(); break; case "quotes": if (status==quotestatus.firstquote) { field.Clear(); } if (status== quotestatus.secondquote) { status =quotestatus.none; } break; } } if (field.Length != 0) { ar.Add(field.ToString()); } return ar; }
Если вы можете гарантировать, что нет запятых в данных, то самый простой способ, вероятно, будет использовать строку.сплит.
например:
String[] values = myString.Split(','); myObject.StringField = values[0]; myObject.IntField = Int32.Parse(values[1]);там могут быть библиотеки, вы могли бы использовать, чтобы помочь, но это наверное так просто, как вы можете получить. Просто убедитесь, что вы не можете иметь пробелы в данных, в противном случае вам придется разобрать его получше.
Comments