Чтение CSV файлов в С#

Кто-нибудь знает библиотеку с открытым исходным кодом, которая позволяет анализировать и читать файлы .csv на С#?

Ответы

Ответ 2

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

  • Одиночная кавычка
  • \-экранированный цитируемый текст
  • альтернативные разделители (пока не будут работать с полями, связанными с трубой или табуляцией)
  • Некотируемые текстовые поля, начинающиеся с цитаты

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

Теперь о GitHub

Обновление: было похоже, что добавление текстовой поддержки с одиночной кавычкой. Это простое изменение, но я набрал его прямо в окне ответа, чтобы он не тестировался. Используйте ссылку ревизии внизу, если вы предпочитаете старый (проверенный) код.

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;
}

Ответ 3

В последний раз этот вопрос задавали, здесь ответ, который я дал:

Если вы просто пытаетесь прочитать 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, но было бы тривиально перевести его на С#.

Ответ 4

Мне очень нравится библиотека FileHelpers. Он быстрый, он С# 100%, он доступен для БЕСПЛАТНО, он очень гибкий и простой в использовании.

Ответ 5

Я выполняю ответ Даниэля Прайдена на С#, так что его легче вырезать, вставлять и настраивать. Я думаю, что это самый простой способ для разбора 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.");
        }
    }
}

Ответ 6

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

Вот пример использования 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();
    }
}

Ответ 7

Вы можете использовать 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;
        }