Ответ 1
Посмотрите Быстрый CSV-ридер на CodeProject.
Кто-нибудь знает библиотеку с открытым исходным кодом, которая позволяет анализировать и читать файлы .csv
на С#?
Посмотрите Быстрый CSV-ридер на CodeProject.
Здесь написано, что вы действительно используете общие коллекции и блоки итераторов. Он поддерживает закрытые текстовые поля с двойными кавычками (включая те, которые охватывают многолистные строки), используя соглашение с двойным экранированием (поэтому ""
внутри поля с кавычками читается как символ одиночной кавычки). Он не поддерживает:
Но все это было бы достаточно легко добавить, если они вам понадобятся. Я нигде не сравнивал его (я бы хотел увидеть некоторые результаты), но производительность должна быть очень хорошей - лучше, чем все, что .Split()
основывалось в любом случае.
Обновление: было похоже, что добавление текстовой поддержки с одиночной кавычкой. Это простое изменение, но я набрал его прямо в окне ответа, чтобы он не тестировался. Используйте ссылку ревизии внизу, если вы предпочитаете старый (проверенный) код.
public static class CSV
{
public static IEnumerable<IList<string>> FromFile(string fileName)
{
foreach (IList<string> item in FromFile(fileName, ignoreFirstLineDefault)) yield return item;
}
public static IEnumerable<IList<string>> FromFile(string fileName, bool ignoreFirstLine)
{
using (StreamReader rdr = new StreamReader(fileName))
{
foreach(IList<string> item in FromReader(rdr, ignoreFirstLine)) yield return item;
}
}
public static IEnumerable<IList<string>> FromStream(Stream csv)
{
foreach (IList<string> item in FromStream(csv, ignoreFirstLineDefault)) yield return item;
}
public static IEnumerable<IList<string>> FromStream(Stream csv, bool ignoreFirstLine)
{
using (var rdr = new StreamReader(csv))
{
foreach (IList<string> item in FromReader(rdr, ignoreFirstLine)) yield return item;
}
}
public static IEnumerable<IList<string>> FromReader(TextReader csv)
{
//Probably should have used TextReader instead of StreamReader
foreach (IList<string> item in FromReader(csv, ignoreFirstLineDefault)) yield return item;
}
public static IEnumerable<IList<string>> FromReader(TextReader csv, bool ignoreFirstLine)
{
if (ignoreFirstLine) csv.ReadLine();
IList<string> result = new List<string>();
StringBuilder curValue = new StringBuilder();
char c;
c = (char)csv.Read();
while (csv.Peek() != -1)
{
switch (c)
{
case ',': //empty field
result.Add("");
c = (char)csv.Read();
break;
case '"': //qualified text
case '\'':
char q = c;
c = (char)csv.Read();
bool inQuotes = true;
while (inQuotes && csv.Peek() != -1)
{
if (c == q)
{
c = (char)csv.Read();
if (c != q)
inQuotes = false;
}
if (inQuotes)
{
curValue.Append(c);
c = (char)csv.Read();
}
}
result.Add(curValue.ToString());
curValue = new StringBuilder();
if (c == ',') c = (char)csv.Read(); // either ',', newline, or endofstream
break;
case '\n': //end of the record
case '\r':
//potential bug here depending on what your line breaks look like
if (result.Count > 0) // don't return empty records
{
yield return result;
result = new List<string>();
}
c = (char)csv.Read();
break;
default: //normal unqualified text
while (c != ',' && c != '\r' && c != '\n' && csv.Peek() != -1)
{
curValue.Append(c);
c = (char)csv.Read();
}
result.Add(curValue.ToString());
curValue = new StringBuilder();
if (c == ',') c = (char)csv.Read(); //either ',', newline, or endofstream
break;
}
}
if (curValue.Length > 0) //potential bug: I don't want to skip on a empty column in the last record if a caller really expects it to be there
result.Add(curValue.ToString());
if (result.Count > 0)
yield return result;
}
private static bool ignoreFirstLineDefault = false;
}
В последний раз этот вопрос задавали, здесь ответ, который я дал:
Если вы просто пытаетесь прочитать CSV файл с С#, проще всего использовать класс Microsoft.VisualBasic.FileIO.TextFieldParser. Он фактически встроен в .NET Framework, вместо того, чтобы быть сторонним расширением.
Да, он находится в Microsoft.VisualBasic.dll
, но это не значит, что вы не можете использовать его с С# (или любым другим языком CLR).
Вот пример использования, взятый из документации MSDN:
Using MyReader As New _
Microsoft.VisualBasic.FileIO.TextFieldParser("C:\testfile.txt")
MyReader.TextFieldType = FileIO.FieldType.Delimited
MyReader.SetDelimiters(",")
Dim currentRow As String()
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
Dim currentField As String
For Each currentField In currentRow
MsgBox(currentField)
Next
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message & _
"is not valid and will be skipped.")
End Try
End While
End Using
Опять же, этот пример находится в VB.NET, но было бы тривиально перевести его на С#.
Мне очень нравится библиотека FileHelpers. Он быстрый, он С# 100%, он доступен для БЕСПЛАТНО, он очень гибкий и простой в использовании.
Я выполняю ответ Даниэля Прайдена на С#, так что его легче вырезать, вставлять и настраивать. Я думаю, что это самый простой способ для разбора CSV файлов. Просто добавьте ссылку, и в основном вы делаете.
Добавьте ссылку Microsoft.VisualBasic
в свой проект
Затем вот пример кода в С# из ответа Джоэля:
using (Microsoft.VisualBasic.FileIO.TextFieldParser MyReader = new
Microsoft.VisualBasic.FileIO.TextFieldParser(filename))
{
MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
MyReader.SetDelimiters(",");
while (!MyReader.EndOfData)
{
try
{
string[] fields = MyReader.ReadFields();
if (first)
{
first = false;
continue;
}
// This is how I treat my data, you'll need to throw this out.
//"Type" "Post Date" "Description" "Amount"
LineItem li = new LineItem();
li.date = DateTime.Parse(fields[1]);
li.description = fields[2];
li.Value = Convert.ToDecimal(fields[3]);
lineitems1.Add(li);
}
catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex)
{
MessageBox.Show("Line " + ex.Message +
" is not valid and will be skipped.");
}
}
}
Помимо разбора/чтения, некоторые библиотеки выполняют другие приятные вещи, такие как преобразование проанализированных данных в объект для вас.
Вот пример использования CsvHelper (библиотека, которую я поддерживаю) для чтения CSV файла в объекты.
var csv = new CsvHelper( File.OpenRead( "file.csv" ) );
var myCustomObjectList = csv.Reader.GetRecords<MyCustomObject>();
По умолчанию соглашения используются для сопоставления заголовков/столбцов со свойствами. Вы можете изменить поведение, изменив настройки.
// Using attributes:
public class MyCustomObject
{
[CsvField( Name = "First Name" )]
public string StringProperty { get; set; }
[CsvField( Index = 0 )]
public int IntProperty { get; set; }
[CsvField( Ignore = true )]
public string ShouldIgnore { get; set; }
}
Иногда вы не "владеете" объектом, который хотите заполнить данными. В этом случае вы можете свободно использовать сопоставление классов.
// Fluent class mapping:
public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
public MyCustomObjectMap()
{
Map( m => m.StringProperty ).Name( "First Name" );
Map( m => m.IntProperty ).Index( 0 );
Map( m => m.ShouldIgnore ).Ignore();
}
}
Вы можете использовать Microsoft.VisualBasic.FileIO.TextFieldParser
получить ниже пример кода из вышеприведенной статьи
static void Main()
{
string [email protected]"C:\Users\Administrator\Desktop\test.csv";
DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);
Console.WriteLine("Rows count:" + csvData.Rows.Count);
Console.ReadLine();
}
private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
{
DataTable csvData = new DataTable();
try
{
using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
{
csvReader.SetDelimiters(new string[] { "," });
csvReader.HasFieldsEnclosedInQuotes = true;
string[] colFields = csvReader.ReadFields();
foreach (string column in colFields)
{
DataColumn datecolumn = new DataColumn(column);
datecolumn.AllowDBNull = true;
csvData.Columns.Add(datecolumn);
}
while (!csvReader.EndOfData)
{
string[] fieldData = csvReader.ReadFields();
//Making empty value as null
for (int i = 0; i < fieldData.Length; i++)
{
if (fieldData[i] == "")
{
fieldData[i] = null;
}
}
csvData.Rows.Add(fieldData);
}
}
}
catch (Exception ex)
{
}
return csvData;
}